@chalksurf/cli 0.3.4 → 0.3.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/README.md +5 -3
- package/dist/bin/chalksurf.js +1519 -278
- package/docs/agents.md +18 -2
- package/docs/manual.md +37 -3
- package/docs/mcp.md +9 -1
- package/package.json +1 -1
package/dist/bin/chalksurf.js
CHANGED
|
@@ -151,6 +151,15 @@ var createApiClient = ({
|
|
|
151
151
|
agentGetExerciseUsage: async (id) => {
|
|
152
152
|
return await query("agentGetExerciseUsage", { id });
|
|
153
153
|
},
|
|
154
|
+
agentListQualityIssues: async (input) => {
|
|
155
|
+
return await query("agentListQualityIssues", input);
|
|
156
|
+
},
|
|
157
|
+
agentResolveQualityIssue: async (input) => {
|
|
158
|
+
return await mutation("agentResolveQualityIssue", input);
|
|
159
|
+
},
|
|
160
|
+
agentDismissQualityIssue: async (input) => {
|
|
161
|
+
return await mutation("agentDismissQualityIssue", input);
|
|
162
|
+
},
|
|
154
163
|
agentListExerciseLabels: async () => {
|
|
155
164
|
return await query("agentListExerciseLabels", {});
|
|
156
165
|
},
|
|
@@ -211,6 +220,9 @@ var createApiClient = ({
|
|
|
211
220
|
agentBulkUpdateSheets: async (input) => {
|
|
212
221
|
return await mutation("agentBulkUpdateSheets", input);
|
|
213
222
|
},
|
|
223
|
+
agentVerifyBulkSheetUpdates: async (input) => {
|
|
224
|
+
return await mutation("agentVerifyBulkSheetUpdates", input);
|
|
225
|
+
},
|
|
214
226
|
agentListSheets: async (input) => {
|
|
215
227
|
return await query("agentListSheets", input);
|
|
216
228
|
},
|
|
@@ -887,7 +899,9 @@ var Constants = {
|
|
|
887
899
|
"generate_sheet_translation_completed",
|
|
888
900
|
"upload_exercise_figure",
|
|
889
901
|
"attach_exercise_figures",
|
|
890
|
-
"bulk_update_sheets"
|
|
902
|
+
"bulk_update_sheets",
|
|
903
|
+
"resolve_quality_issue",
|
|
904
|
+
"dismiss_quality_issue"
|
|
891
905
|
],
|
|
892
906
|
agent_audit_outcome: ["started", "success", "failed", "denied"],
|
|
893
907
|
agent_audit_resource_type: ["exercise", "sheet", "folder", "job", "event", "figure_asset"],
|
|
@@ -928,7 +942,6 @@ var Constants = {
|
|
|
928
942
|
"figure_update",
|
|
929
943
|
"automatic_labeling"
|
|
930
944
|
],
|
|
931
|
-
figure_asset_source_kind: ["document_crop", "rendered_figure"],
|
|
932
945
|
job_type: [
|
|
933
946
|
"exercise_sheet_import",
|
|
934
947
|
"exercise_import",
|
|
@@ -1408,6 +1421,18 @@ var agentWriteOperationRegistry = {
|
|
|
1408
1421
|
resourceType: "exercise",
|
|
1409
1422
|
sync: true
|
|
1410
1423
|
},
|
|
1424
|
+
resolve_quality_issue: {
|
|
1425
|
+
agentToolName: "resolve_quality_issue",
|
|
1426
|
+
receiptOperation: "resolve_quality_issue",
|
|
1427
|
+
resourceType: "exercise",
|
|
1428
|
+
sync: true
|
|
1429
|
+
},
|
|
1430
|
+
dismiss_quality_issue: {
|
|
1431
|
+
agentToolName: "dismiss_quality_issue",
|
|
1432
|
+
receiptOperation: "dismiss_quality_issue",
|
|
1433
|
+
resourceType: "exercise",
|
|
1434
|
+
sync: true
|
|
1435
|
+
},
|
|
1411
1436
|
update_sheet: {
|
|
1412
1437
|
agentToolName: "update_sheet",
|
|
1413
1438
|
receiptOperation: "update_sheet",
|
|
@@ -1735,6 +1760,7 @@ var folderPathSegmentSchema = strictObject({
|
|
|
1735
1760
|
name: z3.string().trim().min(1)
|
|
1736
1761
|
});
|
|
1737
1762
|
var listSheetsAgentInputSchema = strictObject({
|
|
1763
|
+
afterId: uuidSchema.optional(),
|
|
1738
1764
|
folderId: uuidSchema.optional(),
|
|
1739
1765
|
includeDescendants: z3.boolean().optional().default(false),
|
|
1740
1766
|
limit: z3.number().int().min(1).max(100).optional().default(50),
|
|
@@ -1742,6 +1768,9 @@ var listSheetsAgentInputSchema = strictObject({
|
|
|
1742
1768
|
}).refine(({ folderId, includeDescendants }) => folderId !== void 0 || !includeDescendants, {
|
|
1743
1769
|
message: "folderId is required when includeDescendants is true",
|
|
1744
1770
|
path: ["folderId"]
|
|
1771
|
+
}).refine(({ afterId, offset }) => afterId === void 0 || offset === 0, {
|
|
1772
|
+
message: "offset must be zero when afterId is provided",
|
|
1773
|
+
path: ["offset"]
|
|
1745
1774
|
});
|
|
1746
1775
|
var listSheetsAgentOutputSchema = strictObject({
|
|
1747
1776
|
sheets: z3.array(
|
|
@@ -1756,6 +1785,7 @@ var listSheetsAgentOutputSchema = strictObject({
|
|
|
1756
1785
|
seriesAssignments: sheetSeriesAssignmentsSchema
|
|
1757
1786
|
})
|
|
1758
1787
|
),
|
|
1788
|
+
inventoryToken: z3.string().trim().min(1),
|
|
1759
1789
|
totalCount: z3.number().int().min(0)
|
|
1760
1790
|
});
|
|
1761
1791
|
var exerciseLabelSchema = strictObject({
|
|
@@ -2008,6 +2038,41 @@ var getExerciseUsageAgentInputSchema = strictObject({
|
|
|
2008
2038
|
var getExerciseUsageAgentOutputSchema = strictObject({
|
|
2009
2039
|
usage: exerciseUsageSchema
|
|
2010
2040
|
});
|
|
2041
|
+
var qualityIssueSelectorShape = {
|
|
2042
|
+
exerciseIds: z3.array(uuidSchema).min(1).max(100).optional(),
|
|
2043
|
+
issueIds: z3.array(uuidSchema).min(1).max(100).optional(),
|
|
2044
|
+
severities: z3.array(z3.enum(exerciseQualityIssueSeverities)).min(1).optional(),
|
|
2045
|
+
limit: z3.number().int().min(1).max(100).optional().default(50),
|
|
2046
|
+
cursor: z3.string().trim().min(1).optional()
|
|
2047
|
+
};
|
|
2048
|
+
var listQualityIssuesAgentInputSchema = strictObject(qualityIssueSelectorShape).refine(
|
|
2049
|
+
({ exerciseIds, issueIds }) => exerciseIds === void 0 || issueIds === void 0,
|
|
2050
|
+
{ message: "exerciseIds and issueIds are mutually exclusive" }
|
|
2051
|
+
);
|
|
2052
|
+
var qualityIssueListItemSchema = strictObject({
|
|
2053
|
+
id: uuidSchema,
|
|
2054
|
+
severity: z3.enum(exerciseQualityIssueSeverities),
|
|
2055
|
+
comment: z3.string(),
|
|
2056
|
+
sourceLanguage: z3.enum(translationLanguages),
|
|
2057
|
+
sourceInputHash: z3.string(),
|
|
2058
|
+
createdAt: dateStringSchema,
|
|
2059
|
+
exercise: strictObject({
|
|
2060
|
+
id: uuidSchema,
|
|
2061
|
+
updatedAt: dateStringSchema,
|
|
2062
|
+
subject: z3.enum(exerciseSubjects),
|
|
2063
|
+
source: z3.string().nullable(),
|
|
2064
|
+
organizationId: uuidSchema
|
|
2065
|
+
})
|
|
2066
|
+
});
|
|
2067
|
+
var listQualityIssuesAgentOutputSchema = strictObject({
|
|
2068
|
+
issues: z3.array(qualityIssueListItemSchema),
|
|
2069
|
+
nextCursor: z3.string().nullable()
|
|
2070
|
+
});
|
|
2071
|
+
var resolveQualityIssueAgentInputSchema = strictObject({
|
|
2072
|
+
issueId: uuidSchema,
|
|
2073
|
+
expectedExerciseUpdatedAt: expectedUpdatedAtSchema
|
|
2074
|
+
});
|
|
2075
|
+
var dismissQualityIssueAgentInputSchema = resolveQualityIssueAgentInputSchema;
|
|
2011
2076
|
var agentWriteReceiptSchema = strictObject({
|
|
2012
2077
|
resource: strictObject({
|
|
2013
2078
|
type: z3.enum(["exercise", "sheet", "folder"]),
|
|
@@ -2033,6 +2098,8 @@ var agentWriteReceiptSchema = strictObject({
|
|
|
2033
2098
|
var agentWriteReceiptOutputSchema = strictObject({
|
|
2034
2099
|
receipt: agentWriteReceiptSchema
|
|
2035
2100
|
});
|
|
2101
|
+
var resolveQualityIssueAgentOutputSchema = agentWriteReceiptOutputSchema;
|
|
2102
|
+
var dismissQualityIssueAgentOutputSchema = agentWriteReceiptOutputSchema;
|
|
2036
2103
|
var copyExerciseAgentInputSchema = strictObject({
|
|
2037
2104
|
id: uuidSchema
|
|
2038
2105
|
});
|
|
@@ -2218,6 +2285,34 @@ var bulkUpdateSheetsAgentOutputSchema = strictObject({
|
|
|
2218
2285
|
results: z3.array(bulkUpdateSheetResultSchema),
|
|
2219
2286
|
receipt: agentWriteReceiptSchema.nullable()
|
|
2220
2287
|
});
|
|
2288
|
+
var verifyBulkSheetUpdateStatusSchema = z3.enum(["applied", "pending", "conflicting", "error"]);
|
|
2289
|
+
var verifyBulkSheetUpdateConflictSchema = strictObject({
|
|
2290
|
+
field: z3.enum(["expectedUpdatedAt", "expectedSeriesAssignments"]),
|
|
2291
|
+
code: z3.enum(["updated_at_changed", "series_assignments_changed"])
|
|
2292
|
+
});
|
|
2293
|
+
var verifyBulkSheetUpdateResultSchema = strictObject({
|
|
2294
|
+
id: uuidSchema,
|
|
2295
|
+
status: verifyBulkSheetUpdateStatusSchema,
|
|
2296
|
+
matchedPaths: z3.array(z3.string()),
|
|
2297
|
+
unmatchedPaths: z3.array(z3.string()),
|
|
2298
|
+
conflicts: z3.array(verifyBulkSheetUpdateConflictSchema),
|
|
2299
|
+
errorCode: z3.string().nullable(),
|
|
2300
|
+
errorMessage: z3.string().nullable()
|
|
2301
|
+
});
|
|
2302
|
+
var verifyBulkSheetUpdatesAgentInputSchema = strictObject({
|
|
2303
|
+
updates: z3.array(updateSheetAgentInputSchema).min(1).max(100)
|
|
2304
|
+
});
|
|
2305
|
+
var verifyBulkSheetUpdatesAgentOutputSchema = strictObject({
|
|
2306
|
+
state: z3.enum(["applied", "pending", "conflicting", "mixed", "error"]),
|
|
2307
|
+
counts: strictObject({
|
|
2308
|
+
total: z3.number().int().min(0),
|
|
2309
|
+
applied: z3.number().int().min(0),
|
|
2310
|
+
pending: z3.number().int().min(0),
|
|
2311
|
+
conflicting: z3.number().int().min(0),
|
|
2312
|
+
error: z3.number().int().min(0)
|
|
2313
|
+
}),
|
|
2314
|
+
results: z3.array(verifyBulkSheetUpdateResultSchema)
|
|
2315
|
+
});
|
|
2221
2316
|
var createSheetScoredExerciseSchema = strictObject({
|
|
2222
2317
|
id: uuidSchema,
|
|
2223
2318
|
scorePoints: z3.number().min(0).max(99999.9).refine((value) => Number.isInteger(value * 10), "Score can have at most 1 decimal place").nullable()
|
|
@@ -2611,6 +2706,18 @@ var getExerciseUsageInputSchema = getExerciseUsageAgentInputSchema.extend({
|
|
|
2611
2706
|
organizationId: organizationIdSchema
|
|
2612
2707
|
});
|
|
2613
2708
|
var getExerciseUsageOutputSchema = getExerciseUsageAgentOutputSchema;
|
|
2709
|
+
var listQualityIssuesInputSchema = listQualityIssuesAgentInputSchema.safeExtend({
|
|
2710
|
+
organizationId: organizationIdSchema
|
|
2711
|
+
});
|
|
2712
|
+
var listQualityIssuesOutputSchema = listQualityIssuesAgentOutputSchema;
|
|
2713
|
+
var resolveQualityIssueInputSchema = resolveQualityIssueAgentInputSchema.extend({
|
|
2714
|
+
organizationId: organizationIdSchema
|
|
2715
|
+
});
|
|
2716
|
+
var resolveQualityIssueOutputSchema = resolveQualityIssueAgentOutputSchema;
|
|
2717
|
+
var dismissQualityIssueInputSchema = dismissQualityIssueAgentInputSchema.extend({
|
|
2718
|
+
organizationId: organizationIdSchema
|
|
2719
|
+
});
|
|
2720
|
+
var dismissQualityIssueOutputSchema = dismissQualityIssueAgentOutputSchema;
|
|
2614
2721
|
var listExerciseLabelsInputSchema = listExerciseLabelsAgentInputSchema;
|
|
2615
2722
|
var listExerciseLabelsOutputSchema = listExerciseLabelsAgentOutputSchema;
|
|
2616
2723
|
var copyExerciseInputSchema = copyExerciseAgentInputSchema.extend({
|
|
@@ -2687,7 +2794,7 @@ var searchSheetsOutputSchema = strictObject2({
|
|
|
2687
2794
|
var listSheetsInputSchema = listSheetsAgentInputSchema.extend({
|
|
2688
2795
|
organizationId: organizationIdSchema
|
|
2689
2796
|
});
|
|
2690
|
-
var listSheetsOutputSchema = listSheetsAgentOutputSchema;
|
|
2797
|
+
var listSheetsOutputSchema = listSheetsAgentOutputSchema.omit({ inventoryToken: true });
|
|
2691
2798
|
var getSheetInputSchema = getSheetAgentInputSchema.extend({
|
|
2692
2799
|
organizationId: organizationIdSchema
|
|
2693
2800
|
});
|
|
@@ -2726,6 +2833,10 @@ var bulkUpdateSheetsInputSchema = bulkUpdateSheetsAgentInputSchema.extend({
|
|
|
2726
2833
|
organizationId: organizationIdSchema
|
|
2727
2834
|
});
|
|
2728
2835
|
var bulkUpdateSheetsOutputSchema = bulkUpdateSheetsAgentOutputSchema;
|
|
2836
|
+
var verifyBulkSheetUpdatesInputSchema = verifyBulkSheetUpdatesAgentInputSchema.extend({
|
|
2837
|
+
organizationId: organizationIdSchema
|
|
2838
|
+
});
|
|
2839
|
+
var verifyBulkSheetUpdatesOutputSchema = verifyBulkSheetUpdatesAgentOutputSchema;
|
|
2729
2840
|
var setSheetVisibilityInputSchema = setSheetVisibilityAgentInputSchema.extend({
|
|
2730
2841
|
organizationId: organizationIdSchema
|
|
2731
2842
|
});
|
|
@@ -3587,6 +3698,16 @@ var chalksurfMcpToolCapabilities = {
|
|
|
3587
3698
|
requiredPermission: "read",
|
|
3588
3699
|
security: { type: "oauth2", scopes: [chalksurfMcpScopes.read] }
|
|
3589
3700
|
},
|
|
3701
|
+
list_quality_issues: {
|
|
3702
|
+
name: "list_quality_issues",
|
|
3703
|
+
title: "List quality issues",
|
|
3704
|
+
description: "List active quality issues for exercises owned by the selected ChalkSurf organization.",
|
|
3705
|
+
inputSchema: listQualityIssuesInputSchema,
|
|
3706
|
+
outputSchema: listQualityIssuesOutputSchema,
|
|
3707
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
3708
|
+
requiredPermission: "read",
|
|
3709
|
+
security: { type: "oauth2", scopes: [chalksurfMcpScopes.read] }
|
|
3710
|
+
},
|
|
3590
3711
|
list_exercise_labels: {
|
|
3591
3712
|
name: "list_exercise_labels",
|
|
3592
3713
|
title: "List exercise labels",
|
|
@@ -3637,6 +3758,26 @@ var chalksurfMcpToolCapabilities = {
|
|
|
3637
3758
|
requiredPermission: "write",
|
|
3638
3759
|
security: { type: "oauth2", scopes: [chalksurfMcpScopes.write] }
|
|
3639
3760
|
},
|
|
3761
|
+
resolve_quality_issue: {
|
|
3762
|
+
name: "resolve_quality_issue",
|
|
3763
|
+
title: "Resolve quality issue",
|
|
3764
|
+
description: "Mark an active quality issue resolved after checking the exercise evidence timestamp.",
|
|
3765
|
+
inputSchema: resolveQualityIssueInputSchema,
|
|
3766
|
+
outputSchema: resolveQualityIssueOutputSchema,
|
|
3767
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
3768
|
+
requiredPermission: "write",
|
|
3769
|
+
security: { type: "oauth2", scopes: [chalksurfMcpScopes.write] }
|
|
3770
|
+
},
|
|
3771
|
+
dismiss_quality_issue: {
|
|
3772
|
+
name: "dismiss_quality_issue",
|
|
3773
|
+
title: "Dismiss quality issue",
|
|
3774
|
+
description: "Dismiss an active quality issue after checking the exercise evidence timestamp.",
|
|
3775
|
+
inputSchema: dismissQualityIssueInputSchema,
|
|
3776
|
+
outputSchema: dismissQualityIssueOutputSchema,
|
|
3777
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
3778
|
+
requiredPermission: "write",
|
|
3779
|
+
security: { type: "oauth2", scopes: [chalksurfMcpScopes.write] }
|
|
3780
|
+
},
|
|
3640
3781
|
set_exercise_visibility: {
|
|
3641
3782
|
name: "set_exercise_visibility",
|
|
3642
3783
|
title: "Set exercise visibility",
|
|
@@ -3801,6 +3942,16 @@ var chalksurfMcpToolCapabilities = {
|
|
|
3801
3942
|
requiredPermission: "write",
|
|
3802
3943
|
security: { type: "oauth2", scopes: [chalksurfMcpScopes.write] }
|
|
3803
3944
|
},
|
|
3945
|
+
verify_bulk_sheet_updates: {
|
|
3946
|
+
name: "verify_bulk_sheet_updates",
|
|
3947
|
+
title: "Verify bulk sheet updates",
|
|
3948
|
+
description: "Use this after an uncertain bulk apply response to classify each requested sheet target as applied, pending, conflicting, or error without writing.",
|
|
3949
|
+
inputSchema: verifyBulkSheetUpdatesInputSchema,
|
|
3950
|
+
outputSchema: verifyBulkSheetUpdatesOutputSchema,
|
|
3951
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
3952
|
+
requiredPermission: "write",
|
|
3953
|
+
security: { type: "oauth2", scopes: [chalksurfMcpScopes.write] }
|
|
3954
|
+
},
|
|
3804
3955
|
set_sheet_visibility: {
|
|
3805
3956
|
name: "set_sheet_visibility",
|
|
3806
3957
|
title: "Set sheet visibility",
|
|
@@ -8873,10 +9024,46 @@ var parseBulkUpdateSheetsAgentInput = (input) => {
|
|
|
8873
9024
|
}
|
|
8874
9025
|
return parsedInput.data;
|
|
8875
9026
|
};
|
|
9027
|
+
var parseVerifyBulkSheetUpdatesAgentInput = (input) => {
|
|
9028
|
+
if (typeof input === "object" && input !== null && "schemaVersion" in input) {
|
|
9029
|
+
throw new CliCommandError(
|
|
9030
|
+
"CLI result envelopes are not verifier manifests. Pass the original bulk update manifest or an object with updates.",
|
|
9031
|
+
2
|
|
9032
|
+
);
|
|
9033
|
+
}
|
|
9034
|
+
if (typeof input === "object" && input !== null && "mode" in input) {
|
|
9035
|
+
const parsedBulkInput = bulkUpdateSheetsAgentInputSchema.safeParse(input);
|
|
9036
|
+
if (!parsedBulkInput.success) {
|
|
9037
|
+
throw new CliCommandError(
|
|
9038
|
+
`Invalid bulk sheet verification input: ${formatZodIssues(parsedBulkInput.error.issues)}`,
|
|
9039
|
+
2
|
|
9040
|
+
);
|
|
9041
|
+
}
|
|
9042
|
+
return { updates: parsedBulkInput.data.updates };
|
|
9043
|
+
}
|
|
9044
|
+
const parsedInput = verifyBulkSheetUpdatesAgentInputSchema.safeParse(input);
|
|
9045
|
+
if (!parsedInput.success) {
|
|
9046
|
+
throw new CliCommandError(`Invalid bulk sheet verification input: ${formatZodIssues(parsedInput.error.issues)}`, 2);
|
|
9047
|
+
}
|
|
9048
|
+
return parsedInput.data;
|
|
9049
|
+
};
|
|
8876
9050
|
|
|
8877
9051
|
// src/lib/agent-receipt.ts
|
|
9052
|
+
var getBulkSheetCount = (details) => {
|
|
9053
|
+
if (typeof details !== "object" || details === null || !("sheetIds" in details) || !Array.isArray(details.sheetIds)) {
|
|
9054
|
+
return null;
|
|
9055
|
+
}
|
|
9056
|
+
return details.sheetIds.filter((sheetId) => typeof sheetId === "string").length;
|
|
9057
|
+
};
|
|
8878
9058
|
var formatAgentWriteReceiptOutput = ({ receipt }) => {
|
|
8879
|
-
|
|
9059
|
+
if (receipt.resource) {
|
|
9060
|
+
return `${receipt.operation} -> ${receipt.resource.type} ${receipt.resource.id}`;
|
|
9061
|
+
}
|
|
9062
|
+
const bulkSheetCount = receipt.operation === "bulk_update_sheets" ? getBulkSheetCount(receipt.details) : null;
|
|
9063
|
+
if (bulkSheetCount !== null) {
|
|
9064
|
+
return `${receipt.operation} -> ${bulkSheetCount} sheet${bulkSheetCount === 1 ? "" : "s"}`;
|
|
9065
|
+
}
|
|
9066
|
+
return `${receipt.operation} completed`;
|
|
8880
9067
|
};
|
|
8881
9068
|
|
|
8882
9069
|
// src/lib/command-options.ts
|
|
@@ -12034,135 +12221,1047 @@ var registerProfileCommands = (profileYargs, context) => {
|
|
|
12034
12221
|
).demandCommand(1).strict();
|
|
12035
12222
|
};
|
|
12036
12223
|
|
|
12037
|
-
// src/commands/
|
|
12224
|
+
// src/commands/quality-issue/index.ts
|
|
12225
|
+
import { z as z15 } from "zod";
|
|
12226
|
+
|
|
12227
|
+
// src/commands/quality-issue/artifact-integrity.ts
|
|
12228
|
+
import { createHash } from "node:crypto";
|
|
12229
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
12230
|
+
import { join as join2 } from "node:path";
|
|
12231
|
+
|
|
12232
|
+
// src/commands/quality-issue/artifact-schemas.ts
|
|
12038
12233
|
import { z as z14 } from "zod";
|
|
12039
|
-
var
|
|
12040
|
-
|
|
12041
|
-
|
|
12042
|
-
|
|
12043
|
-
|
|
12044
|
-
|
|
12234
|
+
var schemaVersion = z14.literal("v1");
|
|
12235
|
+
var uuid = z14.uuid();
|
|
12236
|
+
var commonShape = {
|
|
12237
|
+
schemaVersion,
|
|
12238
|
+
runId: z14.string().trim().min(1),
|
|
12239
|
+
organizationId: uuid
|
|
12240
|
+
};
|
|
12241
|
+
var qualityIssueScopeSchema = z14.object({
|
|
12242
|
+
...commonShape,
|
|
12243
|
+
request: z14.string().trim().min(1),
|
|
12244
|
+
environment: z14.string().trim().min(1),
|
|
12245
|
+
profile: z14.string().trim().min(1),
|
|
12246
|
+
mode: z14.enum(["dry_run", "apply"]),
|
|
12247
|
+
discovery: z14.object({}).catchall(z14.unknown())
|
|
12248
|
+
}).strict();
|
|
12249
|
+
var qualityIssueInventoryRowSchema = z14.object({
|
|
12250
|
+
...commonShape,
|
|
12251
|
+
issueId: uuid,
|
|
12252
|
+
exerciseId: uuid,
|
|
12253
|
+
exerciseUpdatedAt: z14.string().trim().min(1),
|
|
12254
|
+
severity: z14.enum(["warning", "error", "critical"]),
|
|
12255
|
+
subject: z14.enum(["math", "physics", "chemistry"]),
|
|
12256
|
+
evidence: z14.object({}).catchall(z14.unknown())
|
|
12257
|
+
}).strict();
|
|
12258
|
+
var qualityIssueDiscoveryRowSchema = z14.object({
|
|
12259
|
+
...commonShape,
|
|
12260
|
+
tool: z14.string().trim().min(1),
|
|
12261
|
+
evidence: z14.object({}).catchall(z14.unknown())
|
|
12262
|
+
}).strict();
|
|
12263
|
+
var qualityIssueCandidateExerciseRowSchema = z14.object({
|
|
12264
|
+
...commonShape,
|
|
12265
|
+
exerciseId: uuid,
|
|
12266
|
+
evidence: z14.object({}).catchall(z14.unknown())
|
|
12267
|
+
}).strict();
|
|
12268
|
+
var patchSchema = z14.object({
|
|
12269
|
+
path: z14.string().trim().min(1),
|
|
12270
|
+
before: z14.unknown(),
|
|
12271
|
+
after: z14.unknown()
|
|
12272
|
+
}).strict().refine((patch) => Object.hasOwn(patch, "before") && Object.hasOwn(patch, "after"), {
|
|
12273
|
+
message: "Patch before and after values are required; use null for an intentionally empty value."
|
|
12274
|
+
});
|
|
12275
|
+
var classificationBaseShape = {
|
|
12276
|
+
...commonShape,
|
|
12277
|
+
issueId: uuid,
|
|
12278
|
+
reasonCode: z14.string().trim().min(1),
|
|
12279
|
+
rationale: z14.string().trim().min(1)
|
|
12280
|
+
};
|
|
12281
|
+
var qualityIssueClassificationRowSchema = z14.discriminatedUnion("disposition", [
|
|
12282
|
+
z14.object({
|
|
12283
|
+
...classificationBaseShape,
|
|
12284
|
+
disposition: z14.literal("proposed_fix"),
|
|
12285
|
+
patches: z14.array(patchSchema).min(1)
|
|
12286
|
+
}).strict(),
|
|
12287
|
+
z14.object({ ...classificationBaseShape, disposition: z14.literal("proposed_dismissal") }).strict(),
|
|
12288
|
+
z14.object({ ...classificationBaseShape, disposition: z14.literal("needs_human") }).strict(),
|
|
12289
|
+
z14.object({ ...classificationBaseShape, disposition: z14.literal("stale") }).strict(),
|
|
12290
|
+
z14.object({ ...classificationBaseShape, disposition: z14.literal("failed") }).strict(),
|
|
12291
|
+
z14.object({ ...classificationBaseShape, disposition: z14.literal("skipped") }).strict()
|
|
12292
|
+
]);
|
|
12293
|
+
var actionIdentityShape = {
|
|
12294
|
+
actionId: z14.string().trim().min(1),
|
|
12295
|
+
exerciseId: uuid
|
|
12296
|
+
};
|
|
12297
|
+
var updateActionSchema = z14.object({
|
|
12298
|
+
...actionIdentityShape,
|
|
12299
|
+
type: z14.literal("update_exercise"),
|
|
12300
|
+
expectedExerciseUpdatedAt: z14.string().trim().min(1),
|
|
12301
|
+
issueIds: z14.array(uuid).min(1),
|
|
12302
|
+
patches: z14.array(patchSchema).min(1)
|
|
12303
|
+
}).strict();
|
|
12304
|
+
var resolveActionSchema = z14.object({
|
|
12305
|
+
...actionIdentityShape,
|
|
12306
|
+
type: z14.literal("resolve_quality_issue"),
|
|
12307
|
+
issueId: uuid,
|
|
12308
|
+
expectedExerciseUpdatedAtFromActionId: z14.string().trim().min(1)
|
|
12309
|
+
}).strict();
|
|
12310
|
+
var dismissActionSchema = z14.object({
|
|
12311
|
+
...actionIdentityShape,
|
|
12312
|
+
type: z14.literal("dismiss_quality_issue"),
|
|
12313
|
+
issueId: uuid,
|
|
12314
|
+
expectedExerciseUpdatedAt: z14.string().trim().min(1)
|
|
12315
|
+
}).strict();
|
|
12316
|
+
var qualityIssueActionPlanSchema = z14.object({
|
|
12317
|
+
...commonShape,
|
|
12318
|
+
inventory: z14.array(
|
|
12319
|
+
z14.object({
|
|
12320
|
+
issueId: uuid,
|
|
12321
|
+
exerciseId: uuid,
|
|
12322
|
+
exerciseUpdatedAt: z14.string().trim().min(1)
|
|
12323
|
+
}).strict()
|
|
12324
|
+
),
|
|
12325
|
+
actions: z14.array(z14.discriminatedUnion("type", [updateActionSchema, resolveActionSchema, dismissActionSchema])),
|
|
12326
|
+
exclusions: z14.array(
|
|
12327
|
+
z14.object({
|
|
12328
|
+
issueId: uuid,
|
|
12329
|
+
disposition: z14.enum(["needs_human", "stale", "failed", "skipped"]),
|
|
12330
|
+
reasonCode: z14.string().trim().min(1)
|
|
12331
|
+
}).strict()
|
|
12332
|
+
)
|
|
12333
|
+
}).strict();
|
|
12334
|
+
var resultBaseShape = {
|
|
12335
|
+
...commonShape,
|
|
12336
|
+
actionId: z14.string().trim().min(1)
|
|
12337
|
+
};
|
|
12338
|
+
var terminalErrorSchema = z14.object({
|
|
12339
|
+
code: z14.string().trim().min(1),
|
|
12340
|
+
message: z14.string().trim().min(1),
|
|
12341
|
+
recovery: z14.string().trim().min(1)
|
|
12342
|
+
}).strict();
|
|
12343
|
+
var terminalResultSchema = (outcome) => z14.object({
|
|
12344
|
+
...resultBaseShape,
|
|
12345
|
+
outcome: z14.literal(outcome),
|
|
12346
|
+
error: terminalErrorSchema
|
|
12347
|
+
}).strict();
|
|
12348
|
+
var qualityIssueApplicationResultRowSchema = z14.discriminatedUnion("outcome", [
|
|
12349
|
+
z14.object({
|
|
12350
|
+
...resultBaseShape,
|
|
12351
|
+
outcome: z14.literal("success"),
|
|
12352
|
+
receipt: agentWriteReceiptSchema,
|
|
12353
|
+
resultingExerciseUpdatedAt: z14.iso.datetime({ offset: true }).optional()
|
|
12354
|
+
}).strict(),
|
|
12355
|
+
terminalResultSchema("failed"),
|
|
12356
|
+
terminalResultSchema("stale"),
|
|
12357
|
+
terminalResultSchema("skipped")
|
|
12358
|
+
]);
|
|
12359
|
+
|
|
12360
|
+
// src/commands/quality-issue/artifact-integrity.ts
|
|
12361
|
+
var readJson = async (path, schema) => {
|
|
12362
|
+
try {
|
|
12363
|
+
return schema.parse(JSON.parse(await readFile5(path, "utf8")));
|
|
12364
|
+
} catch (error) {
|
|
12365
|
+
throw new CliCommandError(`Artifact validation failed for ${path}: ${String(error)}`, 2, true, {
|
|
12366
|
+
code: "artifact_validation_failed",
|
|
12367
|
+
retryable: false
|
|
12368
|
+
});
|
|
12369
|
+
}
|
|
12045
12370
|
};
|
|
12046
|
-
var
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
|
|
12050
|
-
})
|
|
12051
|
-
|
|
12052
|
-
|
|
12371
|
+
var readJsonLines = async (path, schema, optional = false) => {
|
|
12372
|
+
let contents;
|
|
12373
|
+
try {
|
|
12374
|
+
contents = await readFile5(path, "utf8");
|
|
12375
|
+
} catch (error) {
|
|
12376
|
+
if (optional && typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
12377
|
+
return [];
|
|
12378
|
+
}
|
|
12379
|
+
throw new CliCommandError(`Artifact validation failed for ${path}: ${String(error)}`, 2, true, {
|
|
12380
|
+
code: "artifact_validation_failed",
|
|
12381
|
+
retryable: false
|
|
12382
|
+
});
|
|
12053
12383
|
}
|
|
12054
|
-
return {
|
|
12055
|
-
|
|
12056
|
-
|
|
12057
|
-
|
|
12058
|
-
|
|
12059
|
-
|
|
12060
|
-
|
|
12061
|
-
|
|
12384
|
+
return contents.split(/\r?\n/u).filter((line) => line.trim().length > 0).map((line, index) => {
|
|
12385
|
+
try {
|
|
12386
|
+
return schema.parse(JSON.parse(line));
|
|
12387
|
+
} catch (error) {
|
|
12388
|
+
throw new CliCommandError(`Artifact validation failed for ${path}:${index + 1}: ${String(error)}`, 2, true, {
|
|
12389
|
+
code: "artifact_validation_failed",
|
|
12390
|
+
retryable: false
|
|
12391
|
+
});
|
|
12392
|
+
}
|
|
12393
|
+
});
|
|
12062
12394
|
};
|
|
12063
|
-
var
|
|
12064
|
-
const
|
|
12065
|
-
|
|
12066
|
-
|
|
12395
|
+
var assertUnique = (values, label) => {
|
|
12396
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12397
|
+
for (const value of values) {
|
|
12398
|
+
if (seen.has(value)) {
|
|
12399
|
+
throw new CliCommandError(`Artifact validation failed: duplicate ${label} ${value}`, 2, true, {
|
|
12400
|
+
code: "artifact_validation_failed",
|
|
12401
|
+
retryable: false
|
|
12402
|
+
});
|
|
12403
|
+
}
|
|
12404
|
+
seen.add(value);
|
|
12067
12405
|
}
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12406
|
+
};
|
|
12407
|
+
var canonicalize = (value) => {
|
|
12408
|
+
if (Array.isArray(value)) {
|
|
12409
|
+
return value.map(canonicalize);
|
|
12410
|
+
}
|
|
12411
|
+
if (value !== null && typeof value === "object") {
|
|
12412
|
+
return Object.fromEntries(
|
|
12413
|
+
Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, nestedValue]) => [key, canonicalize(nestedValue)])
|
|
12073
12414
|
);
|
|
12074
12415
|
}
|
|
12075
|
-
return
|
|
12416
|
+
return value;
|
|
12076
12417
|
};
|
|
12077
|
-
var
|
|
12078
|
-
|
|
12079
|
-
|
|
12418
|
+
var canonicalJson = (value) => JSON.stringify(canonicalize(value));
|
|
12419
|
+
var assertPartitionIdentity = (scope, rows) => {
|
|
12420
|
+
if (rows.some((row) => row.runId !== scope.runId || row.organizationId !== scope.organizationId)) {
|
|
12421
|
+
throw new CliCommandError("Artifact validation failed: partition identity mismatch", 2, true, {
|
|
12422
|
+
code: "artifact_validation_failed",
|
|
12423
|
+
retryable: false
|
|
12424
|
+
});
|
|
12080
12425
|
}
|
|
12081
|
-
|
|
12082
|
-
|
|
12426
|
+
};
|
|
12427
|
+
var validateQualityIssueRun = async (runDirectory) => {
|
|
12428
|
+
const scope = await readJson(join2(runDirectory, "scope.json"), qualityIssueScopeSchema);
|
|
12429
|
+
const discovery = await readJsonLines(join2(runDirectory, "discovery.jsonl"), qualityIssueDiscoveryRowSchema);
|
|
12430
|
+
const candidates = await readJsonLines(
|
|
12431
|
+
join2(runDirectory, "candidate-exercises.jsonl"),
|
|
12432
|
+
qualityIssueCandidateExerciseRowSchema
|
|
12433
|
+
);
|
|
12434
|
+
const inventory = await readJsonLines(join2(runDirectory, "inventory.jsonl"), qualityIssueInventoryRowSchema);
|
|
12435
|
+
const classifications = await readJsonLines(
|
|
12436
|
+
join2(runDirectory, "classifications.jsonl"),
|
|
12437
|
+
qualityIssueClassificationRowSchema
|
|
12438
|
+
);
|
|
12439
|
+
const actionPlan = await readJson(join2(runDirectory, "action-plan.json"), qualityIssueActionPlanSchema);
|
|
12440
|
+
const results = await readJsonLines(
|
|
12441
|
+
join2(runDirectory, "application-results.jsonl"),
|
|
12442
|
+
qualityIssueApplicationResultRowSchema,
|
|
12443
|
+
true
|
|
12444
|
+
);
|
|
12445
|
+
assertPartitionIdentity(scope, [
|
|
12446
|
+
...discovery,
|
|
12447
|
+
...candidates,
|
|
12448
|
+
...inventory,
|
|
12449
|
+
...classifications,
|
|
12450
|
+
actionPlan,
|
|
12451
|
+
...results
|
|
12452
|
+
]);
|
|
12453
|
+
assertUnique(
|
|
12454
|
+
candidates.map((row) => row.exerciseId),
|
|
12455
|
+
"candidate exerciseId"
|
|
12456
|
+
);
|
|
12457
|
+
assertUnique(
|
|
12458
|
+
inventory.map((row) => row.issueId),
|
|
12459
|
+
"inventory issueId"
|
|
12460
|
+
);
|
|
12461
|
+
assertUnique(
|
|
12462
|
+
classifications.map((row) => row.issueId),
|
|
12463
|
+
"classification issueId"
|
|
12464
|
+
);
|
|
12465
|
+
assertUnique(
|
|
12466
|
+
actionPlan.actions.map((action) => action.actionId),
|
|
12467
|
+
"actionId"
|
|
12468
|
+
);
|
|
12469
|
+
const inventoryIds = new Set(inventory.map((row) => row.issueId));
|
|
12470
|
+
const classificationIds = new Set(classifications.map((row) => row.issueId));
|
|
12471
|
+
if (inventoryIds.size !== classificationIds.size || [...inventoryIds].some((issueId) => !classificationIds.has(issueId))) {
|
|
12472
|
+
throw new CliCommandError(
|
|
12473
|
+
"Artifact validation failed: every inventory issue needs exactly one classification",
|
|
12474
|
+
2,
|
|
12475
|
+
true,
|
|
12476
|
+
{
|
|
12477
|
+
code: "artifact_validation_failed",
|
|
12478
|
+
retryable: false
|
|
12479
|
+
}
|
|
12480
|
+
);
|
|
12083
12481
|
}
|
|
12084
|
-
const
|
|
12085
|
-
|
|
12086
|
-
|
|
12482
|
+
const planInventoryIds = new Set(actionPlan.inventory.map((row) => row.issueId));
|
|
12483
|
+
assertUnique(
|
|
12484
|
+
actionPlan.inventory.map((row) => row.issueId),
|
|
12485
|
+
"action-plan inventory issueId"
|
|
12486
|
+
);
|
|
12487
|
+
if (inventoryIds.size !== planInventoryIds.size || [...inventoryIds].some((issueId) => !planInventoryIds.has(issueId))) {
|
|
12488
|
+
throw new CliCommandError(
|
|
12489
|
+
"Artifact validation failed: action-plan inventory does not match inventory.jsonl",
|
|
12490
|
+
2,
|
|
12491
|
+
true,
|
|
12492
|
+
{
|
|
12493
|
+
code: "artifact_validation_failed",
|
|
12494
|
+
retryable: false
|
|
12495
|
+
}
|
|
12496
|
+
);
|
|
12087
12497
|
}
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12092
|
-
|
|
12093
|
-
|
|
12094
|
-
return normalizedSegments.length > 0 && normalizedSegments.every((segment) => segment !== "." && segment !== "..");
|
|
12095
|
-
};
|
|
12096
|
-
var assertValidSheetImportPlan = (plan) => {
|
|
12097
|
-
if (plan.sheets.length === 0) {
|
|
12098
|
-
throw new CliCommandError("Invalid sheet import plan: sheets must include at least one sheet.", 2);
|
|
12498
|
+
const candidateExerciseIds = new Set(candidates.map((row) => row.exerciseId));
|
|
12499
|
+
if (inventory.some((row) => !candidateExerciseIds.has(row.exerciseId))) {
|
|
12500
|
+
throw new CliCommandError("Artifact validation failed: inventory exercise is missing from candidates", 2, true, {
|
|
12501
|
+
code: "artifact_validation_failed",
|
|
12502
|
+
retryable: false
|
|
12503
|
+
});
|
|
12099
12504
|
}
|
|
12100
|
-
|
|
12101
|
-
|
|
12102
|
-
|
|
12103
|
-
}
|
|
12104
|
-
|
|
12105
|
-
|
|
12106
|
-
|
|
12107
|
-
|
|
12108
|
-
|
|
12109
|
-
|
|
12110
|
-
|
|
12111
|
-
|
|
12505
|
+
const inventoryByIssueId = new Map(inventory.map((row) => [row.issueId, row]));
|
|
12506
|
+
const classificationByIssueId = new Map(classifications.map((row) => [row.issueId, row]));
|
|
12507
|
+
const patchKeys = actionPlan.actions.flatMap(
|
|
12508
|
+
(action) => action.type === "update_exercise" ? action.patches.map((patch) => `${action.exerciseId}\0${patch.path}`) : []
|
|
12509
|
+
);
|
|
12510
|
+
assertUnique(patchKeys, "exercise patch path");
|
|
12511
|
+
assertUnique(
|
|
12512
|
+
classifications.flatMap((classification) => {
|
|
12513
|
+
if (classification.disposition !== "proposed_fix") return [];
|
|
12514
|
+
const inventoryRow = inventoryByIssueId.get(classification.issueId);
|
|
12515
|
+
return classification.patches.map((patch) => `${inventoryRow?.exerciseId ?? ""}\0${patch.path}`);
|
|
12516
|
+
}),
|
|
12517
|
+
"classified exercise patch path"
|
|
12518
|
+
);
|
|
12519
|
+
for (const plannedIssue of actionPlan.inventory) {
|
|
12520
|
+
const source = inventoryByIssueId.get(plannedIssue.issueId);
|
|
12521
|
+
if (!source || source.exerciseId !== plannedIssue.exerciseId || source.exerciseUpdatedAt !== plannedIssue.exerciseUpdatedAt) {
|
|
12522
|
+
throw new CliCommandError("Artifact validation failed: action-plan evidence differs from inventory", 2, true, {
|
|
12523
|
+
code: "artifact_validation_failed",
|
|
12524
|
+
retryable: false
|
|
12525
|
+
});
|
|
12526
|
+
}
|
|
12527
|
+
}
|
|
12528
|
+
const updateActionsByExerciseId = /* @__PURE__ */ new Map();
|
|
12529
|
+
const updateActionsByIssueId = /* @__PURE__ */ new Map();
|
|
12530
|
+
const resolveActionsByIssueId = /* @__PURE__ */ new Map();
|
|
12531
|
+
const dismissActionsByIssueId = /* @__PURE__ */ new Map();
|
|
12532
|
+
const actionIndexById = new Map(actionPlan.actions.map((action, index) => [action.actionId, index]));
|
|
12533
|
+
for (const action of actionPlan.actions) {
|
|
12534
|
+
if (action.type === "update_exercise") {
|
|
12535
|
+
assertUnique(action.issueIds, `update action ${action.actionId} issueId`);
|
|
12536
|
+
if (updateActionsByExerciseId.has(action.exerciseId)) {
|
|
12112
12537
|
throw new CliCommandError(
|
|
12113
|
-
|
|
12114
|
-
2
|
|
12538
|
+
`Artifact validation failed: exercise ${action.exerciseId} has more than one update action`,
|
|
12539
|
+
2,
|
|
12540
|
+
true,
|
|
12541
|
+
{ code: "artifact_validation_failed", retryable: false }
|
|
12115
12542
|
);
|
|
12116
12543
|
}
|
|
12117
|
-
|
|
12118
|
-
|
|
12119
|
-
|
|
12120
|
-
|
|
12121
|
-
if (!
|
|
12544
|
+
updateActionsByExerciseId.set(action.exerciseId, action);
|
|
12545
|
+
const expectedPatches = action.issueIds.flatMap((actionIssueId) => {
|
|
12546
|
+
const source2 = inventoryByIssueId.get(actionIssueId);
|
|
12547
|
+
const classification = classificationByIssueId.get(actionIssueId);
|
|
12548
|
+
if (!source2 || source2.exerciseId !== action.exerciseId || source2.exerciseUpdatedAt !== action.expectedExerciseUpdatedAt || classification?.disposition !== "proposed_fix") {
|
|
12122
12549
|
throw new CliCommandError(
|
|
12123
|
-
"
|
|
12124
|
-
2
|
|
12125
|
-
|
|
12126
|
-
|
|
12127
|
-
|
|
12128
|
-
|
|
12129
|
-
|
|
12130
|
-
if (component.targetFolderPath != null && !isValidTargetFolderPath2(component.targetFolderPath)) {
|
|
12131
|
-
throw new CliCommandError(
|
|
12132
|
-
'Invalid sheet import plan: component targetFolderPath must be a normalized folder path without "." or ".." segments.',
|
|
12133
|
-
2
|
|
12550
|
+
"Artifact validation failed: update action evidence differs from inventory",
|
|
12551
|
+
2,
|
|
12552
|
+
true,
|
|
12553
|
+
{
|
|
12554
|
+
code: "artifact_validation_failed",
|
|
12555
|
+
retryable: false
|
|
12556
|
+
}
|
|
12134
12557
|
);
|
|
12135
12558
|
}
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
}
|
|
12559
|
+
updateActionsByIssueId.set(actionIssueId, [...updateActionsByIssueId.get(actionIssueId) ?? [], action]);
|
|
12560
|
+
return classification.patches;
|
|
12139
12561
|
});
|
|
12140
|
-
|
|
12562
|
+
const sortPatches = (patches) => [...patches].sort((left, right) => left.path.localeCompare(right.path));
|
|
12563
|
+
if (canonicalJson(sortPatches(action.patches)) !== canonicalJson(sortPatches(expectedPatches))) {
|
|
12564
|
+
throw new CliCommandError(
|
|
12565
|
+
`Artifact validation failed: update action patches do not match classifications for ${action.exerciseId}`,
|
|
12566
|
+
2,
|
|
12567
|
+
true,
|
|
12568
|
+
{ code: "artifact_validation_failed", retryable: false }
|
|
12569
|
+
);
|
|
12570
|
+
}
|
|
12571
|
+
continue;
|
|
12141
12572
|
}
|
|
12142
|
-
|
|
12143
|
-
|
|
12573
|
+
const source = inventoryByIssueId.get(action.issueId);
|
|
12574
|
+
if (!source || source.exerciseId !== action.exerciseId) {
|
|
12575
|
+
throw new CliCommandError("Artifact validation failed: triage action evidence differs from inventory", 2, true, {
|
|
12576
|
+
code: "artifact_validation_failed",
|
|
12577
|
+
retryable: false
|
|
12578
|
+
});
|
|
12144
12579
|
}
|
|
12145
|
-
if (
|
|
12580
|
+
if (action.type === "resolve_quality_issue") {
|
|
12581
|
+
resolveActionsByIssueId.set(action.issueId, [...resolveActionsByIssueId.get(action.issueId) ?? [], action]);
|
|
12582
|
+
} else {
|
|
12583
|
+
if (source.exerciseUpdatedAt !== action.expectedExerciseUpdatedAt) {
|
|
12584
|
+
throw new CliCommandError("Artifact validation failed: dismissal evidence differs from inventory", 2, true, {
|
|
12585
|
+
code: "artifact_validation_failed",
|
|
12586
|
+
retryable: false
|
|
12587
|
+
});
|
|
12588
|
+
}
|
|
12589
|
+
dismissActionsByIssueId.set(action.issueId, [...dismissActionsByIssueId.get(action.issueId) ?? [], action]);
|
|
12590
|
+
}
|
|
12591
|
+
}
|
|
12592
|
+
const exclusionsByIssueId = new Map(actionPlan.exclusions.map((exclusion) => [exclusion.issueId, exclusion]));
|
|
12593
|
+
assertUnique(
|
|
12594
|
+
actionPlan.exclusions.map((exclusion) => exclusion.issueId),
|
|
12595
|
+
"exclusion issueId"
|
|
12596
|
+
);
|
|
12597
|
+
for (const classification of classifications) {
|
|
12598
|
+
const source = inventoryByIssueId.get(classification.issueId);
|
|
12599
|
+
const updates = updateActionsByIssueId.get(classification.issueId) ?? [];
|
|
12600
|
+
const resolves = resolveActionsByIssueId.get(classification.issueId) ?? [];
|
|
12601
|
+
const dismissals = dismissActionsByIssueId.get(classification.issueId) ?? [];
|
|
12602
|
+
const exclusion = exclusionsByIssueId.get(classification.issueId);
|
|
12603
|
+
const exerciseUpdate = source ? updateActionsByExerciseId.get(source.exerciseId) : void 0;
|
|
12604
|
+
if (classification.disposition === "proposed_dismissal" && dismissals.length === 1 && exerciseUpdate && (actionIndexById.get(dismissals[0].actionId) ?? Number.POSITIVE_INFINITY) > (actionIndexById.get(exerciseUpdate.actionId) ?? Number.NEGATIVE_INFINITY)) {
|
|
12146
12605
|
throw new CliCommandError(
|
|
12147
|
-
|
|
12148
|
-
2
|
|
12606
|
+
`Artifact validation failed: dismissal ${dismissals[0].actionId} must precede update ${exerciseUpdate.actionId}`,
|
|
12607
|
+
2,
|
|
12608
|
+
true,
|
|
12609
|
+
{ code: "artifact_validation_failed", retryable: false }
|
|
12149
12610
|
);
|
|
12150
12611
|
}
|
|
12151
|
-
|
|
12152
|
-
|
|
12612
|
+
const valid = classification.disposition === "proposed_fix" && !exclusion && updates.length === 1 && resolves.length === 1 && dismissals.length === 0 && resolves[0]?.exerciseId === source?.exerciseId && resolves[0]?.expectedExerciseUpdatedAtFromActionId === updates[0]?.actionId && (actionIndexById.get(updates[0]?.actionId ?? "") ?? Number.POSITIVE_INFINITY) < (actionIndexById.get(resolves[0]?.actionId ?? "") ?? Number.NEGATIVE_INFINITY) || classification.disposition === "proposed_dismissal" && !exclusion && updates.length === 0 && resolves.length === 0 && dismissals.length === 1 || ["needs_human", "stale", "failed", "skipped"].includes(classification.disposition) && updates.length === 0 && resolves.length === 0 && dismissals.length === 0 && exclusion?.disposition === classification.disposition && exclusion.reasonCode === classification.reasonCode;
|
|
12613
|
+
if (!valid) {
|
|
12614
|
+
throw new CliCommandError(
|
|
12615
|
+
`Artifact validation failed: action plan does not exactly implement ${classification.disposition} for ${classification.issueId}`,
|
|
12616
|
+
2,
|
|
12617
|
+
true,
|
|
12618
|
+
{ code: "artifact_validation_failed", retryable: false }
|
|
12619
|
+
);
|
|
12153
12620
|
}
|
|
12154
|
-
});
|
|
12155
|
-
};
|
|
12156
|
-
var normalizeOptionalTitle = (value) => {
|
|
12157
|
-
const normalizedValue = value?.trim();
|
|
12158
|
-
if (!normalizedValue) {
|
|
12159
|
-
return void 0;
|
|
12160
12621
|
}
|
|
12161
|
-
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12622
|
+
if ([...exclusionsByIssueId].some(([excludedIssueId]) => !classificationByIssueId.has(excludedIssueId))) {
|
|
12623
|
+
throw new CliCommandError("Artifact validation failed: action plan contains an unexpected exclusion", 2, true, {
|
|
12624
|
+
code: "artifact_validation_failed",
|
|
12625
|
+
retryable: false
|
|
12626
|
+
});
|
|
12627
|
+
}
|
|
12628
|
+
return { scope, discovery, candidates, inventory, classifications, actionPlan, results };
|
|
12629
|
+
};
|
|
12630
|
+
var digestQualityIssueRun = (run) => createHash("sha256").update(
|
|
12631
|
+
JSON.stringify(
|
|
12632
|
+
canonicalize({
|
|
12633
|
+
scope: run.scope,
|
|
12634
|
+
discovery: run.discovery,
|
|
12635
|
+
candidates: run.candidates,
|
|
12636
|
+
inventory: run.inventory,
|
|
12637
|
+
classifications: run.classifications,
|
|
12638
|
+
actionPlan: run.actionPlan
|
|
12639
|
+
})
|
|
12640
|
+
)
|
|
12641
|
+
).digest("hex");
|
|
12642
|
+
var reconcileQualityIssueRun = ({
|
|
12643
|
+
actionPlan,
|
|
12644
|
+
results
|
|
12645
|
+
}) => {
|
|
12646
|
+
const planned = new Set(actionPlan.actions.map((action) => action.actionId));
|
|
12647
|
+
const counts = /* @__PURE__ */ new Map();
|
|
12648
|
+
for (const result of results) counts.set(result.actionId, (counts.get(result.actionId) ?? 0) + 1);
|
|
12649
|
+
const missingActionIds = [...planned].filter((actionId) => !counts.has(actionId));
|
|
12650
|
+
const duplicateActionIds = [...counts].filter(([, count]) => count > 1).map(([actionId]) => actionId);
|
|
12651
|
+
const unexpectedActionIds = [...counts].filter(([actionId]) => !planned.has(actionId)).map(([actionId]) => actionId);
|
|
12652
|
+
const resultByActionId = new Map(
|
|
12653
|
+
results.filter((result) => counts.get(result.actionId) === 1).map((result) => [result.actionId, result])
|
|
12654
|
+
);
|
|
12655
|
+
const resultIndexByActionId = new Map(results.map((result, index) => [result.actionId, index]));
|
|
12656
|
+
const sameStrings = (left, right) => canonicalJson([...left].sort()) === canonicalJson([...right].sort());
|
|
12657
|
+
const semanticInvalidActionIds = actionPlan.actions.flatMap((action) => {
|
|
12658
|
+
const result = resultByActionId.get(action.actionId);
|
|
12659
|
+
if (!result || result.outcome !== "success") return [];
|
|
12660
|
+
const receipt = result.receipt;
|
|
12661
|
+
if (receipt.resource.type !== "exercise" || receipt.resource.id !== action.exerciseId || receipt.operation !== action.type || receipt.precondition.status !== "matched") {
|
|
12662
|
+
return [action.actionId];
|
|
12663
|
+
}
|
|
12664
|
+
if (action.type === "update_exercise") {
|
|
12665
|
+
const valid2 = result.resultingExerciseUpdatedAt !== void 0 && receipt.precondition.expectedUpdatedAt === action.expectedExerciseUpdatedAt && receipt.precondition.actualUpdatedAt === action.expectedExerciseUpdatedAt && sameStrings(
|
|
12666
|
+
receipt.changedPaths,
|
|
12667
|
+
action.patches.map((patch) => patch.path)
|
|
12668
|
+
);
|
|
12669
|
+
return valid2 ? [] : [action.actionId];
|
|
12670
|
+
}
|
|
12671
|
+
const expectedChangedPaths = [`qualityIssues.${action.issueId}.status`];
|
|
12672
|
+
const details = receipt.details;
|
|
12673
|
+
const expectedStatus = action.type === "resolve_quality_issue" ? "resolved" : "dismissed";
|
|
12674
|
+
if (!sameStrings(receipt.changedPaths, expectedChangedPaths) || typeof details !== "object" || details === null || !("issueId" in details) || details.issueId !== action.issueId || !("status" in details) || details.status !== expectedStatus) {
|
|
12675
|
+
return [action.actionId];
|
|
12676
|
+
}
|
|
12677
|
+
if (action.type === "dismiss_quality_issue") {
|
|
12678
|
+
const valid2 = receipt.precondition.expectedUpdatedAt === action.expectedExerciseUpdatedAt && receipt.precondition.actualUpdatedAt === action.expectedExerciseUpdatedAt;
|
|
12679
|
+
return valid2 ? [] : [action.actionId];
|
|
12680
|
+
}
|
|
12681
|
+
const dependencyResult = resultByActionId.get(action.expectedExerciseUpdatedAtFromActionId);
|
|
12682
|
+
const dependencyIndex = resultIndexByActionId.get(action.expectedExerciseUpdatedAtFromActionId);
|
|
12683
|
+
const resultIndex = resultIndexByActionId.get(action.actionId);
|
|
12684
|
+
const expectedUpdatedAt = dependencyResult?.outcome === "success" ? dependencyResult.resultingExerciseUpdatedAt : void 0;
|
|
12685
|
+
const valid = dependencyResult?.outcome === "success" && expectedUpdatedAt !== void 0 && dependencyIndex !== void 0 && resultIndex !== void 0 && dependencyIndex < resultIndex && receipt.precondition.expectedUpdatedAt === expectedUpdatedAt && receipt.precondition.actualUpdatedAt === expectedUpdatedAt;
|
|
12686
|
+
return valid ? [] : [action.actionId];
|
|
12687
|
+
});
|
|
12688
|
+
const orderInvalidActionIds = missingActionIds.length === 0 && duplicateActionIds.length === 0 && unexpectedActionIds.length === 0 ? actionPlan.actions.flatMap(
|
|
12689
|
+
(action, index) => results[index]?.actionId === action.actionId ? [] : [action.actionId]
|
|
12690
|
+
) : [];
|
|
12691
|
+
const invalidActionIds = [.../* @__PURE__ */ new Set([...semanticInvalidActionIds, ...orderInvalidActionIds])];
|
|
12692
|
+
return {
|
|
12693
|
+
complete: missingActionIds.length === 0 && duplicateActionIds.length === 0 && unexpectedActionIds.length === 0 && invalidActionIds.length === 0,
|
|
12694
|
+
missingActionIds,
|
|
12695
|
+
duplicateActionIds,
|
|
12696
|
+
unexpectedActionIds,
|
|
12697
|
+
invalidActionIds
|
|
12698
|
+
};
|
|
12699
|
+
};
|
|
12700
|
+
var verifyQualityIssueRun = (run, approvedDigest) => {
|
|
12701
|
+
const actualDigest = digestQualityIssueRun(run);
|
|
12702
|
+
if (actualDigest !== approvedDigest) {
|
|
12703
|
+
throw new CliCommandError("Approved digest does not match the current action plan", 2, true, {
|
|
12704
|
+
code: "approval_digest_mismatch",
|
|
12705
|
+
retryable: false
|
|
12706
|
+
});
|
|
12707
|
+
}
|
|
12708
|
+
return actualDigest;
|
|
12709
|
+
};
|
|
12710
|
+
|
|
12711
|
+
// src/commands/quality-issue/index.ts
|
|
12712
|
+
var parseUuid = (value, label) => {
|
|
12713
|
+
const result = z15.uuid().safeParse(value);
|
|
12714
|
+
if (!result.success) throw new CliCommandError(`${label} must be a UUID.`, 2);
|
|
12715
|
+
return result.data;
|
|
12716
|
+
};
|
|
12717
|
+
var parseUuidArray = (values, label) => {
|
|
12718
|
+
if (values === void 0) return void 0;
|
|
12719
|
+
const result = z15.array(z15.uuid()).min(1).max(100).safeParse(values);
|
|
12720
|
+
if (!result.success) throw new CliCommandError(`${label} must contain 1 to 100 UUIDs.`, 2);
|
|
12721
|
+
return result.data;
|
|
12722
|
+
};
|
|
12723
|
+
var registerQualityIssueCommands = (yargs2, context) => yargs2.command(
|
|
12724
|
+
"list",
|
|
12725
|
+
"List active quality issues for organization-owned exercises",
|
|
12726
|
+
(listYargs) => listYargs.option("exercise-id", { type: "string", array: true, describe: "Restrict to exercise IDs" }).option("issue-id", { type: "string", array: true, describe: "Restrict to quality issue IDs" }).option("severity", {
|
|
12727
|
+
type: "string",
|
|
12728
|
+
array: true,
|
|
12729
|
+
choices: ["warning", "error", "critical"]
|
|
12730
|
+
}).option("limit", { type: "number", default: 50 }).option("cursor", { type: "string" }),
|
|
12731
|
+
async (argv) => {
|
|
12732
|
+
const common = argv;
|
|
12733
|
+
if (argv.exerciseId && argv.issueId) {
|
|
12734
|
+
throw new CliCommandError("--exercise-id and --issue-id are mutually exclusive.", 2);
|
|
12735
|
+
}
|
|
12736
|
+
const input = {
|
|
12737
|
+
exerciseIds: parseUuidArray(argv.exerciseId, "exercise-id"),
|
|
12738
|
+
issueIds: parseUuidArray(argv.issueId, "issue-id"),
|
|
12739
|
+
severities: argv.severity,
|
|
12740
|
+
limit: normalizeLimit(argv.limit),
|
|
12741
|
+
cursor: argv.cursor
|
|
12742
|
+
};
|
|
12743
|
+
const { apiClient, organizationId } = await createResolvedApiClient({
|
|
12744
|
+
context,
|
|
12745
|
+
baseUrlFlagValue: common.baseUrl,
|
|
12746
|
+
organizationFlagValue: common.organization,
|
|
12747
|
+
profileName: common.profile
|
|
12748
|
+
});
|
|
12749
|
+
try {
|
|
12750
|
+
const result = await apiClient.agentListQualityIssues(input);
|
|
12751
|
+
context.output.print(
|
|
12752
|
+
{ request: { organizationId, ...input }, ...result },
|
|
12753
|
+
`${result.issues.length} active quality issue(s)${result.nextCursor ? "; more available" : ""}`,
|
|
12754
|
+
{ command: "quality-issue list" }
|
|
12755
|
+
);
|
|
12756
|
+
} catch (error) {
|
|
12757
|
+
throw mapApiErrorToCliError(error);
|
|
12758
|
+
}
|
|
12759
|
+
}
|
|
12760
|
+
).command(
|
|
12761
|
+
"resolve <issueId>",
|
|
12762
|
+
"Resolve one quality issue using the approved exercise timestamp",
|
|
12763
|
+
(commandYargs) => commandYargs.positional("issueId", { type: "string", demandOption: true }).option("expected-exercise-updated-at", { type: "string", demandOption: true }),
|
|
12764
|
+
async (argv) => {
|
|
12765
|
+
const common = argv;
|
|
12766
|
+
const input = {
|
|
12767
|
+
issueId: parseUuid(argv.issueId, "issueId"),
|
|
12768
|
+
expectedExerciseUpdatedAt: argv.expectedExerciseUpdatedAt
|
|
12769
|
+
};
|
|
12770
|
+
const { apiClient } = await createResolvedApiClient({
|
|
12771
|
+
context,
|
|
12772
|
+
baseUrlFlagValue: common.baseUrl,
|
|
12773
|
+
organizationFlagValue: common.organization,
|
|
12774
|
+
profileName: common.profile
|
|
12775
|
+
});
|
|
12776
|
+
try {
|
|
12777
|
+
const result = await apiClient.agentResolveQualityIssue(input);
|
|
12778
|
+
context.output.print(result, formatAgentWriteReceiptOutput, { command: "quality-issue resolve" });
|
|
12779
|
+
} catch (error) {
|
|
12780
|
+
throw mapApiErrorToCliError(error);
|
|
12781
|
+
}
|
|
12782
|
+
}
|
|
12783
|
+
).command(
|
|
12784
|
+
"dismiss <issueId>",
|
|
12785
|
+
"Dismiss one quality issue using the approved exercise timestamp",
|
|
12786
|
+
(commandYargs) => commandYargs.positional("issueId", { type: "string", demandOption: true }).option("expected-exercise-updated-at", { type: "string", demandOption: true }),
|
|
12787
|
+
async (argv) => {
|
|
12788
|
+
const common = argv;
|
|
12789
|
+
const input = {
|
|
12790
|
+
issueId: parseUuid(argv.issueId, "issueId"),
|
|
12791
|
+
expectedExerciseUpdatedAt: argv.expectedExerciseUpdatedAt
|
|
12792
|
+
};
|
|
12793
|
+
const { apiClient } = await createResolvedApiClient({
|
|
12794
|
+
context,
|
|
12795
|
+
baseUrlFlagValue: common.baseUrl,
|
|
12796
|
+
organizationFlagValue: common.organization,
|
|
12797
|
+
profileName: common.profile
|
|
12798
|
+
});
|
|
12799
|
+
try {
|
|
12800
|
+
const result = await apiClient.agentDismissQualityIssue(input);
|
|
12801
|
+
context.output.print(result, formatAgentWriteReceiptOutput, { command: "quality-issue dismiss" });
|
|
12802
|
+
} catch (error) {
|
|
12803
|
+
throw mapApiErrorToCliError(error);
|
|
12804
|
+
}
|
|
12805
|
+
}
|
|
12806
|
+
).command(
|
|
12807
|
+
"artifact <subcommand>",
|
|
12808
|
+
"Validate and reconcile local quality-issue run artifacts",
|
|
12809
|
+
(artifactYargs) => artifactYargs.command(
|
|
12810
|
+
"validate",
|
|
12811
|
+
"Validate a quality-issue run partition",
|
|
12812
|
+
(commandYargs) => commandYargs.option("run-dir", { type: "string", demandOption: true }),
|
|
12813
|
+
async (argv) => {
|
|
12814
|
+
const run = await validateQualityIssueRun(argv.runDir);
|
|
12815
|
+
context.output.print(
|
|
12816
|
+
{
|
|
12817
|
+
runId: run.scope.runId,
|
|
12818
|
+
organizationId: run.scope.organizationId,
|
|
12819
|
+
inventoryCount: run.inventory.length,
|
|
12820
|
+
classificationCount: run.classifications.length,
|
|
12821
|
+
actionCount: run.actionPlan.actions.length,
|
|
12822
|
+
resultCount: run.results.length
|
|
12823
|
+
},
|
|
12824
|
+
"Quality-issue run artifacts are valid.",
|
|
12825
|
+
{ command: "quality-issue artifact validate" }
|
|
12826
|
+
);
|
|
12827
|
+
}
|
|
12828
|
+
).command(
|
|
12829
|
+
"digest",
|
|
12830
|
+
"Compute the canonical action-plan digest",
|
|
12831
|
+
(commandYargs) => commandYargs.option("run-dir", { type: "string", demandOption: true }),
|
|
12832
|
+
async (argv) => {
|
|
12833
|
+
const run = await validateQualityIssueRun(argv.runDir);
|
|
12834
|
+
const digest = digestQualityIssueRun(run);
|
|
12835
|
+
context.output.print({ digest }, digest, { command: "quality-issue artifact digest" });
|
|
12836
|
+
}
|
|
12837
|
+
).command(
|
|
12838
|
+
"verify",
|
|
12839
|
+
"Verify an approved action-plan digest before writes",
|
|
12840
|
+
(commandYargs) => commandYargs.option("run-dir", { type: "string", demandOption: true }).option("approved-digest", { type: "string", demandOption: true }),
|
|
12841
|
+
async (argv) => {
|
|
12842
|
+
const run = await validateQualityIssueRun(argv.runDir);
|
|
12843
|
+
const digest = verifyQualityIssueRun(run, argv.approvedDigest);
|
|
12844
|
+
context.output.print({ digest, verified: true }, "Approved digest verified.", {
|
|
12845
|
+
command: "quality-issue artifact verify"
|
|
12846
|
+
});
|
|
12847
|
+
}
|
|
12848
|
+
).command(
|
|
12849
|
+
"reconcile",
|
|
12850
|
+
"Reconcile planned actions with terminal result rows",
|
|
12851
|
+
(commandYargs) => commandYargs.option("run-dir", { type: "string", demandOption: true }),
|
|
12852
|
+
async (argv) => {
|
|
12853
|
+
const run = await validateQualityIssueRun(argv.runDir);
|
|
12854
|
+
const reconciliation = reconcileQualityIssueRun(run);
|
|
12855
|
+
context.output.print(
|
|
12856
|
+
reconciliation,
|
|
12857
|
+
reconciliation.complete ? "Reconciliation complete." : "Reconciliation incomplete.",
|
|
12858
|
+
{
|
|
12859
|
+
command: "quality-issue artifact reconcile",
|
|
12860
|
+
ok: reconciliation.complete
|
|
12861
|
+
}
|
|
12862
|
+
);
|
|
12863
|
+
if (!reconciliation.complete) throwSilentExitCode(2);
|
|
12864
|
+
}
|
|
12865
|
+
).demandCommand(1),
|
|
12866
|
+
() => {
|
|
12867
|
+
}
|
|
12868
|
+
).demandCommand(1);
|
|
12869
|
+
|
|
12870
|
+
// src/commands/sheet.ts
|
|
12871
|
+
import { z as z16 } from "zod";
|
|
12872
|
+
|
|
12873
|
+
// src/lib/result-artifact.ts
|
|
12874
|
+
import { randomUUID } from "node:crypto";
|
|
12875
|
+
import { link, open as open2, unlink } from "node:fs/promises";
|
|
12876
|
+
import { basename as basename3, dirname as dirname2, join as join3, resolve as resolve7 } from "node:path";
|
|
12877
|
+
|
|
12878
|
+
// src/lib/output.ts
|
|
12879
|
+
var cliJsonSchemaVersion = "v1";
|
|
12880
|
+
var ensureTrailingNewline = (value) => {
|
|
12881
|
+
return value.endsWith("\n") ? value : `${value}
|
|
12882
|
+
`;
|
|
12883
|
+
};
|
|
12884
|
+
var writeLine = (writer, value) => {
|
|
12885
|
+
writer.write(ensureTrailingNewline(value));
|
|
12886
|
+
};
|
|
12887
|
+
var serializeCliOutputEnvelope = (envelope) => JSON.stringify(envelope, null, 2);
|
|
12888
|
+
var createCliOutputEnvelope = ({
|
|
12889
|
+
command,
|
|
12890
|
+
error,
|
|
12891
|
+
ok,
|
|
12892
|
+
result,
|
|
12893
|
+
warnings
|
|
12894
|
+
}) => ({
|
|
12895
|
+
schemaVersion: cliJsonSchemaVersion,
|
|
12896
|
+
command,
|
|
12897
|
+
ok,
|
|
12898
|
+
...result === void 0 ? {} : { result },
|
|
12899
|
+
...error ? { error } : {},
|
|
12900
|
+
warnings
|
|
12901
|
+
});
|
|
12902
|
+
var createOutput = ({
|
|
12903
|
+
json,
|
|
12904
|
+
stdout,
|
|
12905
|
+
stderr
|
|
12906
|
+
}) => {
|
|
12907
|
+
let warnings = [];
|
|
12908
|
+
const consumeWarnings = () => {
|
|
12909
|
+
const nextWarnings = warnings;
|
|
12910
|
+
warnings = [];
|
|
12911
|
+
return nextWarnings;
|
|
12912
|
+
};
|
|
12913
|
+
return {
|
|
12914
|
+
json,
|
|
12915
|
+
print: (value, humanFormatter, options) => {
|
|
12916
|
+
if (json) {
|
|
12917
|
+
writeLine(
|
|
12918
|
+
stdout,
|
|
12919
|
+
serializeCliOutputEnvelope(
|
|
12920
|
+
createCliOutputEnvelope({
|
|
12921
|
+
command: options.command,
|
|
12922
|
+
error: options.error,
|
|
12923
|
+
ok: options.ok ?? true,
|
|
12924
|
+
result: value,
|
|
12925
|
+
warnings: consumeWarnings()
|
|
12926
|
+
})
|
|
12927
|
+
)
|
|
12928
|
+
);
|
|
12929
|
+
return;
|
|
12930
|
+
}
|
|
12931
|
+
const message = typeof humanFormatter === "function" ? humanFormatter(value) : humanFormatter;
|
|
12932
|
+
writeLine(stdout, message);
|
|
12933
|
+
},
|
|
12934
|
+
printWithResultArtifact: async (value, compactValue, humanFormatter, options) => {
|
|
12935
|
+
const currentWarnings = [...warnings];
|
|
12936
|
+
await options.publishArtifact(
|
|
12937
|
+
createCliOutputEnvelope({
|
|
12938
|
+
command: options.command,
|
|
12939
|
+
error: options.error,
|
|
12940
|
+
ok: options.ok ?? true,
|
|
12941
|
+
result: value,
|
|
12942
|
+
warnings: currentWarnings
|
|
12943
|
+
})
|
|
12944
|
+
);
|
|
12945
|
+
warnings = warnings.slice(currentWarnings.length);
|
|
12946
|
+
if (json) {
|
|
12947
|
+
writeLine(
|
|
12948
|
+
stdout,
|
|
12949
|
+
serializeCliOutputEnvelope(
|
|
12950
|
+
createCliOutputEnvelope({
|
|
12951
|
+
command: options.command,
|
|
12952
|
+
error: options.error,
|
|
12953
|
+
ok: options.ok ?? true,
|
|
12954
|
+
result: compactValue,
|
|
12955
|
+
warnings: currentWarnings
|
|
12956
|
+
})
|
|
12957
|
+
)
|
|
12958
|
+
);
|
|
12959
|
+
return;
|
|
12960
|
+
}
|
|
12961
|
+
const message = typeof humanFormatter === "function" ? humanFormatter(compactValue) : humanFormatter;
|
|
12962
|
+
writeLine(stdout, message);
|
|
12963
|
+
},
|
|
12964
|
+
printError: ({ command, error, result }) => {
|
|
12965
|
+
if (json) {
|
|
12966
|
+
writeLine(
|
|
12967
|
+
stdout,
|
|
12968
|
+
serializeCliOutputEnvelope(
|
|
12969
|
+
createCliOutputEnvelope({
|
|
12970
|
+
command,
|
|
12971
|
+
ok: false,
|
|
12972
|
+
result,
|
|
12973
|
+
error,
|
|
12974
|
+
warnings: consumeWarnings()
|
|
12975
|
+
})
|
|
12976
|
+
)
|
|
12977
|
+
);
|
|
12978
|
+
return;
|
|
12979
|
+
}
|
|
12980
|
+
writeLine(stderr, formatSerializableCliErrorForHuman(error));
|
|
12981
|
+
},
|
|
12982
|
+
info: (message) => {
|
|
12983
|
+
if (json) {
|
|
12984
|
+
warnings.push(message);
|
|
12985
|
+
return;
|
|
12986
|
+
}
|
|
12987
|
+
writeLine(stderr, message);
|
|
12988
|
+
},
|
|
12989
|
+
error: (message) => {
|
|
12990
|
+
writeLine(stderr, message);
|
|
12991
|
+
}
|
|
12992
|
+
};
|
|
12993
|
+
};
|
|
12994
|
+
|
|
12995
|
+
// src/lib/result-artifact.ts
|
|
12996
|
+
var resolveCliResultFilePath = ({ cwd, inputPath }) => resolve7(cwd, inputPath);
|
|
12997
|
+
var getFilesystemErrorCode = (error) => error.code;
|
|
12998
|
+
var publishCliResultArtifact = async ({
|
|
12999
|
+
destinationPath,
|
|
13000
|
+
envelope
|
|
13001
|
+
}) => {
|
|
13002
|
+
const temporaryPath = join3(dirname2(destinationPath), `.${basename3(destinationPath)}.${randomUUID()}.tmp`);
|
|
13003
|
+
let temporaryFile;
|
|
13004
|
+
try {
|
|
13005
|
+
temporaryFile = await open2(temporaryPath, "wx", 384);
|
|
13006
|
+
await temporaryFile.writeFile(`${serializeCliOutputEnvelope(envelope)}
|
|
13007
|
+
`, "utf8");
|
|
13008
|
+
await temporaryFile.sync();
|
|
13009
|
+
await temporaryFile.close();
|
|
13010
|
+
temporaryFile = void 0;
|
|
13011
|
+
await link(temporaryPath, destinationPath);
|
|
13012
|
+
} catch (error) {
|
|
13013
|
+
if (getFilesystemErrorCode(error) === "EEXIST") {
|
|
13014
|
+
throw new CliCommandError(`Result file "${destinationPath}" already exists; choose another path.`, 2);
|
|
13015
|
+
}
|
|
13016
|
+
const message = error instanceof Error ? error.message : "Unknown filesystem error";
|
|
13017
|
+
throw new CliCommandError(`Could not write result file "${destinationPath}": ${message}`, 1);
|
|
13018
|
+
} finally {
|
|
13019
|
+
await temporaryFile?.close().catch(() => {
|
|
13020
|
+
});
|
|
13021
|
+
await unlink(temporaryPath).catch(() => {
|
|
13022
|
+
});
|
|
13023
|
+
}
|
|
13024
|
+
};
|
|
13025
|
+
|
|
13026
|
+
// src/lib/sheet-list-all.ts
|
|
13027
|
+
var pageSize = 100;
|
|
13028
|
+
var collectAllSheets = async ({
|
|
13029
|
+
fetchPage
|
|
13030
|
+
}) => {
|
|
13031
|
+
const sheets = [];
|
|
13032
|
+
const seenSheetIds = /* @__PURE__ */ new Set();
|
|
13033
|
+
const duplicateSheetIds = /* @__PURE__ */ new Set();
|
|
13034
|
+
const seenPageSignatures = /* @__PURE__ */ new Set();
|
|
13035
|
+
const observedTotalCounts = [];
|
|
13036
|
+
const observedInventoryTokens = [];
|
|
13037
|
+
const issues = [];
|
|
13038
|
+
const warnings = [];
|
|
13039
|
+
let afterId;
|
|
13040
|
+
let pagesFetched = 0;
|
|
13041
|
+
let rowsFetched = 0;
|
|
13042
|
+
const addIssue = (issue, warning) => {
|
|
13043
|
+
if (issues.includes(issue)) {
|
|
13044
|
+
return;
|
|
13045
|
+
}
|
|
13046
|
+
issues.push(issue);
|
|
13047
|
+
warnings.push(warning);
|
|
13048
|
+
};
|
|
13049
|
+
while (true) {
|
|
13050
|
+
const page = await fetchPage({ limit: pageSize, ...afterId ? { afterId } : {} });
|
|
13051
|
+
pagesFetched += 1;
|
|
13052
|
+
rowsFetched += page.sheets.length;
|
|
13053
|
+
observedTotalCounts.push(page.totalCount);
|
|
13054
|
+
observedInventoryTokens.push(page.inventoryToken);
|
|
13055
|
+
const firstObservedTotal = observedTotalCounts[0];
|
|
13056
|
+
if (page.totalCount !== firstObservedTotal) {
|
|
13057
|
+
addIssue(
|
|
13058
|
+
"total_count_changed",
|
|
13059
|
+
`Sheet inventory total changed during collection (${firstObservedTotal} -> ${page.totalCount}).`
|
|
13060
|
+
);
|
|
13061
|
+
}
|
|
13062
|
+
const firstInventoryToken = observedInventoryTokens[0];
|
|
13063
|
+
if (page.inventoryToken !== firstInventoryToken) {
|
|
13064
|
+
addIssue(
|
|
13065
|
+
"inventory_changed",
|
|
13066
|
+
`Sheet inventory membership changed during collection (${firstInventoryToken} -> ${page.inventoryToken}).`
|
|
13067
|
+
);
|
|
13068
|
+
}
|
|
13069
|
+
if (page.sheets.length === 0) {
|
|
13070
|
+
if (sheets.length < page.totalCount) {
|
|
13071
|
+
addIssue(
|
|
13072
|
+
"early_empty_page",
|
|
13073
|
+
`Sheet inventory returned an empty page${afterId ? ` after cursor ${afterId}` : ""} before the observed total of ${page.totalCount}.`
|
|
13074
|
+
);
|
|
13075
|
+
}
|
|
13076
|
+
break;
|
|
13077
|
+
}
|
|
13078
|
+
const pageSignature = page.sheets.map(({ id }) => id).join("\n");
|
|
13079
|
+
if (seenPageSignatures.has(pageSignature)) {
|
|
13080
|
+
addIssue(
|
|
13081
|
+
"repeated_page",
|
|
13082
|
+
`Sheet inventory page${afterId ? ` after cursor ${afterId}` : ""} repeated an earlier page; collection stopped.`
|
|
13083
|
+
);
|
|
13084
|
+
break;
|
|
13085
|
+
}
|
|
13086
|
+
seenPageSignatures.add(pageSignature);
|
|
13087
|
+
for (const sheet of page.sheets) {
|
|
13088
|
+
if (seenSheetIds.has(sheet.id)) {
|
|
13089
|
+
duplicateSheetIds.add(sheet.id);
|
|
13090
|
+
continue;
|
|
13091
|
+
}
|
|
13092
|
+
seenSheetIds.add(sheet.id);
|
|
13093
|
+
sheets.push(sheet);
|
|
13094
|
+
}
|
|
13095
|
+
if (duplicateSheetIds.size > 0) {
|
|
13096
|
+
addIssue(
|
|
13097
|
+
"duplicate_sheet_ids",
|
|
13098
|
+
`Sheet inventory pages repeated ${duplicateSheetIds.size} sheet ID${duplicateSheetIds.size === 1 ? "" : "s"}; the snapshot may be incomplete.`
|
|
13099
|
+
);
|
|
13100
|
+
}
|
|
13101
|
+
if (rowsFetched >= page.totalCount) {
|
|
13102
|
+
break;
|
|
13103
|
+
}
|
|
13104
|
+
if (page.sheets.length < pageSize) {
|
|
13105
|
+
addIssue(
|
|
13106
|
+
"early_short_page",
|
|
13107
|
+
`Sheet inventory returned only ${page.sheets.length} sheets${afterId ? ` after cursor ${afterId}` : ""} before the observed total of ${page.totalCount}.`
|
|
13108
|
+
);
|
|
13109
|
+
break;
|
|
13110
|
+
}
|
|
13111
|
+
afterId = page.sheets.at(-1).id;
|
|
13112
|
+
}
|
|
13113
|
+
const totalCount = observedTotalCounts.at(-1) ?? 0;
|
|
13114
|
+
if (sheets.length !== totalCount) {
|
|
13115
|
+
addIssue(
|
|
13116
|
+
"sheet_count_mismatch",
|
|
13117
|
+
`Collected ${sheets.length} unique sheets but the final observed total was ${totalCount}.`
|
|
13118
|
+
);
|
|
13119
|
+
}
|
|
13120
|
+
return {
|
|
13121
|
+
sheets,
|
|
13122
|
+
totalCount,
|
|
13123
|
+
snapshot: {
|
|
13124
|
+
complete: issues.length === 0,
|
|
13125
|
+
pageSize,
|
|
13126
|
+
pagesFetched,
|
|
13127
|
+
observedTotalCounts,
|
|
13128
|
+
observedInventoryTokens,
|
|
13129
|
+
uniqueSheetCount: sheets.length,
|
|
13130
|
+
duplicateSheetIds: [...duplicateSheetIds],
|
|
13131
|
+
issues
|
|
13132
|
+
},
|
|
13133
|
+
warnings
|
|
13134
|
+
};
|
|
13135
|
+
};
|
|
13136
|
+
|
|
13137
|
+
// src/commands/sheet.ts
|
|
13138
|
+
var readinessIssueTypeLabels = {
|
|
13139
|
+
missing_translation: "missing translations",
|
|
13140
|
+
missing_solution: "missing solutions",
|
|
13141
|
+
invalid_latex: "invalid LaTeX",
|
|
13142
|
+
missing_score: "missing scores",
|
|
13143
|
+
exercise_quality_issue: "exercise quality issues"
|
|
13144
|
+
};
|
|
13145
|
+
var normalizeReadinessFilters = ({
|
|
13146
|
+
issueTypes,
|
|
13147
|
+
needsWork,
|
|
13148
|
+
ready
|
|
13149
|
+
}) => {
|
|
13150
|
+
if (needsWork && ready) {
|
|
13151
|
+
throw new CliCommandError("--needs-work and --ready cannot be used together.", 2);
|
|
13152
|
+
}
|
|
13153
|
+
return {
|
|
13154
|
+
hasIssues: needsWork ? true : ready ? false : null,
|
|
13155
|
+
issueTypes: normalizeChoiceValues({
|
|
13156
|
+
allowedValues: sheetReadinessIssueTypes,
|
|
13157
|
+
label: "--issue-type",
|
|
13158
|
+
values: issueTypes
|
|
13159
|
+
}) ?? []
|
|
13160
|
+
};
|
|
13161
|
+
};
|
|
13162
|
+
var normalizeSheetSeriesPath = (value) => {
|
|
13163
|
+
const normalizedValue = normalizeOptionalText(value);
|
|
13164
|
+
if (normalizedValue === null) {
|
|
13165
|
+
return null;
|
|
13166
|
+
}
|
|
13167
|
+
const parsedValue = sheetSeriesPathSchema.safeParse(normalizedValue);
|
|
13168
|
+
if (!parsedValue.success) {
|
|
13169
|
+
throw new CliCommandError(
|
|
13170
|
+
'--series-path is unknown. Run "chalksurf sheet series list --json" to inspect valid paths.',
|
|
13171
|
+
2
|
|
13172
|
+
);
|
|
13173
|
+
}
|
|
13174
|
+
return parsedValue.data;
|
|
13175
|
+
};
|
|
13176
|
+
var normalizeSheetSeriesYearFilter = ({ fromYear, toYear }) => {
|
|
13177
|
+
if (fromYear === void 0 && toYear === void 0) {
|
|
13178
|
+
return null;
|
|
13179
|
+
}
|
|
13180
|
+
if (fromYear !== void 0 && toYear !== void 0 && fromYear > toYear) {
|
|
13181
|
+
throw new CliCommandError("--from-year must be less than or equal to --to-year.", 2);
|
|
13182
|
+
}
|
|
13183
|
+
const parsedFilter = sheetSeriesYearFilterSchema.safeParse({ fromYear, toYear });
|
|
13184
|
+
if (!parsedFilter.success) {
|
|
13185
|
+
throw new CliCommandError("--from-year and --to-year must be four-digit years.", 2);
|
|
13186
|
+
}
|
|
13187
|
+
return parsedFilter.data;
|
|
13188
|
+
};
|
|
13189
|
+
var hasUniqueItems2 = (values) => new Set(values).size === values.length;
|
|
13190
|
+
var sheetImportComponentIdPattern3 = /^[a-zA-Z0-9_-]{1,80}$/;
|
|
13191
|
+
var isValidTargetFolderPath2 = (value) => {
|
|
13192
|
+
const normalizedSegments = value.replaceAll("\\", "/").split("/").filter((segment) => segment.length > 0);
|
|
13193
|
+
return normalizedSegments.length > 0 && normalizedSegments.every((segment) => segment !== "." && segment !== "..");
|
|
13194
|
+
};
|
|
13195
|
+
var assertValidSheetImportPlan = (plan) => {
|
|
13196
|
+
if (plan.sheets.length === 0) {
|
|
13197
|
+
throw new CliCommandError("Invalid sheet import plan: sheets must include at least one sheet.", 2);
|
|
13198
|
+
}
|
|
13199
|
+
plan.sheets.forEach((sheet) => {
|
|
13200
|
+
if (sheet.sourceIndexes.length === 0) {
|
|
13201
|
+
throw new CliCommandError("Invalid sheet import plan: sourceIndexes must include at least one source.", 2);
|
|
13202
|
+
}
|
|
13203
|
+
if (!sheet.sourceIndexes.every((sourceIndex) => Number.isInteger(sourceIndex) && sourceIndex >= 0)) {
|
|
13204
|
+
throw new CliCommandError("Invalid sheet import plan: sourceIndexes must contain only non-negative integers.", 2);
|
|
13205
|
+
}
|
|
13206
|
+
if (!hasUniqueItems2(sheet.sourceIndexes)) {
|
|
13207
|
+
throw new CliCommandError("Invalid sheet import plan: sourceIndexes must be unique within a sheet.", 2);
|
|
13208
|
+
}
|
|
13209
|
+
if (sheet.components) {
|
|
13210
|
+
if (sheet.targetFolderPath !== void 0 || sheet.titleOverride !== void 0 || sheet.translateToLanguages) {
|
|
13211
|
+
throw new CliCommandError(
|
|
13212
|
+
"Invalid sheet import plan: targetFolderPath, titleOverride, and translateToLanguages must be set on components when components are provided.",
|
|
13213
|
+
2
|
|
13214
|
+
);
|
|
13215
|
+
}
|
|
13216
|
+
if (!hasUniqueItems2(sheet.components.map((component) => component.componentId))) {
|
|
13217
|
+
throw new CliCommandError("Invalid sheet import plan: componentId values must be unique within a sheet.", 2);
|
|
13218
|
+
}
|
|
13219
|
+
sheet.components.forEach((component) => {
|
|
13220
|
+
if (!sheetImportComponentIdPattern3.test(component.componentId)) {
|
|
13221
|
+
throw new CliCommandError(
|
|
13222
|
+
"Invalid sheet import plan: componentId must be 1-80 letters, numbers, underscores, or hyphens.",
|
|
13223
|
+
2
|
|
13224
|
+
);
|
|
13225
|
+
}
|
|
13226
|
+
if (!component.description.trim()) {
|
|
13227
|
+
throw new CliCommandError("Invalid sheet import plan: component descriptions must be non-empty.", 2);
|
|
13228
|
+
}
|
|
13229
|
+
if (component.targetFolderPath != null && !isValidTargetFolderPath2(component.targetFolderPath)) {
|
|
13230
|
+
throw new CliCommandError(
|
|
13231
|
+
'Invalid sheet import plan: component targetFolderPath must be a normalized folder path without "." or ".." segments.',
|
|
13232
|
+
2
|
|
13233
|
+
);
|
|
13234
|
+
}
|
|
13235
|
+
if (component.translateToLanguages && !hasUniqueItems2(component.translateToLanguages)) {
|
|
13236
|
+
throw new CliCommandError("Invalid sheet import plan: component translateToLanguages must be unique.", 2);
|
|
13237
|
+
}
|
|
13238
|
+
});
|
|
13239
|
+
return;
|
|
13240
|
+
}
|
|
13241
|
+
if (sheet.targetFolderPath === void 0) {
|
|
13242
|
+
throw new CliCommandError("Invalid sheet import plan: targetFolderPath is required.", 2);
|
|
13243
|
+
}
|
|
13244
|
+
if (sheet.targetFolderPath !== null && !isValidTargetFolderPath2(sheet.targetFolderPath)) {
|
|
13245
|
+
throw new CliCommandError(
|
|
13246
|
+
'Invalid sheet import plan: targetFolderPath must be a normalized folder path without "." or ".." segments.',
|
|
13247
|
+
2
|
|
13248
|
+
);
|
|
13249
|
+
}
|
|
13250
|
+
if (sheet.translateToLanguages && !hasUniqueItems2(sheet.translateToLanguages)) {
|
|
13251
|
+
throw new CliCommandError("Invalid sheet import plan: translateToLanguages must be unique.", 2);
|
|
13252
|
+
}
|
|
13253
|
+
});
|
|
13254
|
+
};
|
|
13255
|
+
var normalizeOptionalTitle = (value) => {
|
|
13256
|
+
const normalizedValue = value?.trim();
|
|
13257
|
+
if (!normalizedValue) {
|
|
13258
|
+
return void 0;
|
|
13259
|
+
}
|
|
13260
|
+
return normalizedValue;
|
|
13261
|
+
};
|
|
13262
|
+
var normalizeRequiredText = ({ label, value }) => {
|
|
13263
|
+
const normalizedValue = value?.trim();
|
|
13264
|
+
if (!normalizedValue) {
|
|
12166
13265
|
throw new CliCommandError(`${label} is required.`, 2);
|
|
12167
13266
|
}
|
|
12168
13267
|
return normalizedValue;
|
|
@@ -12187,7 +13286,7 @@ var resolveRequestedUuid2 = ({
|
|
|
12187
13286
|
}
|
|
12188
13287
|
return void 0;
|
|
12189
13288
|
}
|
|
12190
|
-
const parsedValue =
|
|
13289
|
+
const parsedValue = z16.uuid().safeParse(resolvedValue);
|
|
12191
13290
|
if (!parsedValue.success) {
|
|
12192
13291
|
throw new CliCommandError(`${label} must be a valid UUID.`, 2);
|
|
12193
13292
|
}
|
|
@@ -12198,7 +13297,7 @@ var resolveRequestedUuidList = ({ label, values }) => {
|
|
|
12198
13297
|
throw new CliCommandError(`${label} is required at least once.`, 2);
|
|
12199
13298
|
}
|
|
12200
13299
|
return values.map((value, index) => {
|
|
12201
|
-
const parsedValue =
|
|
13300
|
+
const parsedValue = z16.uuid().safeParse(value.trim());
|
|
12202
13301
|
if (!parsedValue.success) {
|
|
12203
13302
|
throw new CliCommandError(`${label}[${index}] must be a valid UUID.`, 2);
|
|
12204
13303
|
}
|
|
@@ -12607,22 +13706,26 @@ var formatDeleteSheetOutput = ({ receipt }) => {
|
|
|
12607
13706
|
var formatUpdateSheetOutput = ({ receipt }) => {
|
|
12608
13707
|
return formatAgentWriteReceiptOutput({ receipt });
|
|
12609
13708
|
};
|
|
12610
|
-
var
|
|
12611
|
-
|
|
12612
|
-
mode,
|
|
12613
|
-
receipt,
|
|
12614
|
-
results
|
|
12615
|
-
}) => {
|
|
12616
|
-
const summary = results.reduce(
|
|
13709
|
+
var getBulkUpdateSheetsCounts = ({ results }) => {
|
|
13710
|
+
const statusCounts = results.reduce(
|
|
12617
13711
|
(counts, result) => ({
|
|
12618
13712
|
...counts,
|
|
12619
13713
|
[result.status]: counts[result.status] + 1
|
|
12620
13714
|
}),
|
|
12621
13715
|
{ ready: 0, unchanged: 0, updated: 0, error: 0 }
|
|
12622
13716
|
);
|
|
13717
|
+
return { total: results.length, ...statusCounts };
|
|
13718
|
+
};
|
|
13719
|
+
var formatBulkUpdateSheetsOutput = ({
|
|
13720
|
+
confirmationDigest,
|
|
13721
|
+
mode,
|
|
13722
|
+
receipt,
|
|
13723
|
+
results
|
|
13724
|
+
}) => {
|
|
13725
|
+
const counts = getBulkUpdateSheetsCounts({ confirmationDigest, mode, receipt, results });
|
|
12623
13726
|
const lines = [
|
|
12624
|
-
`${mode === "dry_run" ? "Validated" : "Applied"} ${
|
|
12625
|
-
`ready: ${
|
|
13727
|
+
`${mode === "dry_run" ? "Validated" : "Applied"} ${counts.total} sheet update${counts.total === 1 ? "" : "s"}.`,
|
|
13728
|
+
`ready: ${counts.ready} updated: ${counts.updated} unchanged: ${counts.unchanged} errors: ${counts.error}`,
|
|
12626
13729
|
`confirmation digest: ${confirmationDigest}`
|
|
12627
13730
|
];
|
|
12628
13731
|
if (receipt) {
|
|
@@ -12630,6 +13733,161 @@ var formatBulkUpdateSheetsOutput = ({
|
|
|
12630
13733
|
}
|
|
12631
13734
|
return lines.join("\n");
|
|
12632
13735
|
};
|
|
13736
|
+
var formatVerifyBulkSheetUpdatesOutput = ({ counts, state }) => [
|
|
13737
|
+
`Verification state: ${state}.`,
|
|
13738
|
+
`total: ${counts.total} applied: ${counts.applied} pending: ${counts.pending} conflicting: ${counts.conflicting} errors: ${counts.error}`
|
|
13739
|
+
].join("\n");
|
|
13740
|
+
var printWithOptionalResultArtifact = async ({
|
|
13741
|
+
command,
|
|
13742
|
+
compactHumanFormatter,
|
|
13743
|
+
compactValue,
|
|
13744
|
+
context,
|
|
13745
|
+
fullHumanFormatter,
|
|
13746
|
+
fullValue,
|
|
13747
|
+
resultFileInput
|
|
13748
|
+
}) => {
|
|
13749
|
+
if (resultFileInput === void 0) {
|
|
13750
|
+
context.output.print(fullValue, fullHumanFormatter, { command });
|
|
13751
|
+
return;
|
|
13752
|
+
}
|
|
13753
|
+
if (!resultFileInput.trim()) {
|
|
13754
|
+
throw new CliCommandError("--result-file must be a non-empty path.", 2);
|
|
13755
|
+
}
|
|
13756
|
+
const resultFile = resolveCliResultFilePath({ cwd: context.cwd, inputPath: resultFileInput });
|
|
13757
|
+
await context.output.printWithResultArtifact(fullValue, { ...compactValue, resultFile }, compactHumanFormatter, {
|
|
13758
|
+
command,
|
|
13759
|
+
publishArtifact: async (envelope) => await publishCliResultArtifact({
|
|
13760
|
+
destinationPath: resultFile,
|
|
13761
|
+
envelope
|
|
13762
|
+
})
|
|
13763
|
+
});
|
|
13764
|
+
};
|
|
13765
|
+
var configureBulkJsonInputOptions = (commandYargs, description) => commandYargs.option("input", {
|
|
13766
|
+
type: "string",
|
|
13767
|
+
nargs: 1,
|
|
13768
|
+
describe: 'Read the JSON object from a UTF-8 file, or "-" for stdin'
|
|
13769
|
+
}).option("input-json", {
|
|
13770
|
+
type: "string",
|
|
13771
|
+
nargs: 1,
|
|
13772
|
+
describe: description
|
|
13773
|
+
}).option("result-file", {
|
|
13774
|
+
type: "string",
|
|
13775
|
+
nargs: 1,
|
|
13776
|
+
describe: "Publish the complete JSON result envelope to a new private file"
|
|
13777
|
+
});
|
|
13778
|
+
var runBulkUpdateCommand = async (argv, context) => {
|
|
13779
|
+
const args = argv;
|
|
13780
|
+
if (args.resultFile !== void 0 && !args.resultFile.trim()) {
|
|
13781
|
+
throw new CliCommandError("--result-file must be a non-empty path.", 2);
|
|
13782
|
+
}
|
|
13783
|
+
const input = parseBulkUpdateSheetsAgentInput(
|
|
13784
|
+
await loadJsonObjectInput({
|
|
13785
|
+
cwd: context.cwd,
|
|
13786
|
+
inputJson: args.inputJson,
|
|
13787
|
+
inputPath: args.input,
|
|
13788
|
+
stdin: context.stdin
|
|
13789
|
+
})
|
|
13790
|
+
);
|
|
13791
|
+
const { apiClient, organizationId } = await createResolvedApiClient({
|
|
13792
|
+
context,
|
|
13793
|
+
baseUrlFlagValue: args.baseUrl,
|
|
13794
|
+
organizationFlagValue: args.organization,
|
|
13795
|
+
profileName: args.profile
|
|
13796
|
+
});
|
|
13797
|
+
let result;
|
|
13798
|
+
try {
|
|
13799
|
+
result = await apiClient.agentBulkUpdateSheets(input);
|
|
13800
|
+
} catch (error) {
|
|
13801
|
+
throw mapApiErrorToCliError(error);
|
|
13802
|
+
}
|
|
13803
|
+
const fullValue = {
|
|
13804
|
+
request: {
|
|
13805
|
+
...input,
|
|
13806
|
+
organizationId: organizationId ?? null
|
|
13807
|
+
},
|
|
13808
|
+
...result
|
|
13809
|
+
};
|
|
13810
|
+
const counts = getBulkUpdateSheetsCounts(result);
|
|
13811
|
+
try {
|
|
13812
|
+
await printWithOptionalResultArtifact({
|
|
13813
|
+
command: "sheet bulk-update",
|
|
13814
|
+
compactHumanFormatter: ({ confirmationDigest, counts: counts2, mode, receiptPresent, resultFile }) => [
|
|
13815
|
+
`${mode === "dry_run" ? "Validated" : "Applied"} ${counts2.total} sheet update${counts2.total === 1 ? "" : "s"}.`,
|
|
13816
|
+
`ready: ${counts2.ready} updated: ${counts2.updated} unchanged: ${counts2.unchanged} errors: ${counts2.error}`,
|
|
13817
|
+
`confirmation digest: ${confirmationDigest}`,
|
|
13818
|
+
`receipt: ${receiptPresent ? "present" : "none"}`,
|
|
13819
|
+
`full result: ${resultFile}`
|
|
13820
|
+
].join("\n"),
|
|
13821
|
+
compactValue: {
|
|
13822
|
+
mode: result.mode,
|
|
13823
|
+
counts,
|
|
13824
|
+
confirmationDigest: result.confirmationDigest,
|
|
13825
|
+
receiptPresent: result.receipt !== null
|
|
13826
|
+
},
|
|
13827
|
+
context,
|
|
13828
|
+
fullHumanFormatter: (output) => formatBulkUpdateSheetsOutput(output),
|
|
13829
|
+
fullValue,
|
|
13830
|
+
resultFileInput: args.resultFile
|
|
13831
|
+
});
|
|
13832
|
+
} catch (error) {
|
|
13833
|
+
if (input.mode !== "apply") {
|
|
13834
|
+
throw error;
|
|
13835
|
+
}
|
|
13836
|
+
const message = error instanceof Error ? error.message : "Unknown result artifact error";
|
|
13837
|
+
context.output.info(
|
|
13838
|
+
`The server confirmed the bulk apply, but the result artifact could not be published: ${message} Do not retry the apply; preserve this stdout response and use "sheet bulk-update verify" with the original manifest if independent recovery is needed.`
|
|
13839
|
+
);
|
|
13840
|
+
context.output.print(fullValue, (output) => formatBulkUpdateSheetsOutput(output), {
|
|
13841
|
+
command: "sheet bulk-update"
|
|
13842
|
+
});
|
|
13843
|
+
}
|
|
13844
|
+
};
|
|
13845
|
+
var runVerifyBulkSheetUpdatesCommand = async (argv, context) => {
|
|
13846
|
+
const args = argv;
|
|
13847
|
+
const input = parseVerifyBulkSheetUpdatesAgentInput(
|
|
13848
|
+
await loadJsonObjectInput({
|
|
13849
|
+
cwd: context.cwd,
|
|
13850
|
+
inputJson: args.inputJson,
|
|
13851
|
+
inputPath: args.input,
|
|
13852
|
+
stdin: context.stdin
|
|
13853
|
+
})
|
|
13854
|
+
);
|
|
13855
|
+
const { apiClient, organizationId } = await createResolvedApiClient({
|
|
13856
|
+
context,
|
|
13857
|
+
baseUrlFlagValue: args.baseUrl,
|
|
13858
|
+
organizationFlagValue: args.organization,
|
|
13859
|
+
profileName: args.profile
|
|
13860
|
+
});
|
|
13861
|
+
let result;
|
|
13862
|
+
try {
|
|
13863
|
+
result = await apiClient.agentVerifyBulkSheetUpdates(input);
|
|
13864
|
+
} catch (error) {
|
|
13865
|
+
throw mapApiErrorToCliError(error);
|
|
13866
|
+
}
|
|
13867
|
+
const fullValue = {
|
|
13868
|
+
request: {
|
|
13869
|
+
...input,
|
|
13870
|
+
organizationId: organizationId ?? null
|
|
13871
|
+
},
|
|
13872
|
+
...result
|
|
13873
|
+
};
|
|
13874
|
+
await printWithOptionalResultArtifact({
|
|
13875
|
+
command: "sheet bulk-update verify",
|
|
13876
|
+
compactHumanFormatter: ({ counts, resultFile, state }) => [
|
|
13877
|
+
`Verification state: ${state}.`,
|
|
13878
|
+
`total: ${counts.total} applied: ${counts.applied} pending: ${counts.pending} conflicting: ${counts.conflicting} errors: ${counts.error}`,
|
|
13879
|
+
`full result: ${resultFile}`
|
|
13880
|
+
].join("\n"),
|
|
13881
|
+
compactValue: {
|
|
13882
|
+
state: result.state,
|
|
13883
|
+
counts: result.counts
|
|
13884
|
+
},
|
|
13885
|
+
context,
|
|
13886
|
+
fullHumanFormatter: (output) => formatVerifyBulkSheetUpdatesOutput(output),
|
|
13887
|
+
fullValue,
|
|
13888
|
+
resultFileInput: args.resultFile
|
|
13889
|
+
});
|
|
13890
|
+
};
|
|
12633
13891
|
var formatSheetSeriesOutput = ({ series }) => series.map(({ depth, names, path }) => `${" ".repeat(depth)}${names.english} ${path}`).join("\n");
|
|
12634
13892
|
var formatSetSheetVisibilityOutput = ({ receipt }) => {
|
|
12635
13893
|
return formatAgentWriteReceiptOutput({ receipt });
|
|
@@ -12686,13 +13944,25 @@ var registerSheetCommands = (sheetYargs, context) => {
|
|
|
12686
13944
|
describe: "Include sheets in descendant folders; requires --folder-id"
|
|
12687
13945
|
}).option("limit", {
|
|
12688
13946
|
type: "number",
|
|
12689
|
-
default: 50,
|
|
12690
13947
|
describe: "Maximum number of sheets to return"
|
|
12691
13948
|
}).option("offset", {
|
|
12692
13949
|
type: "number",
|
|
12693
|
-
default: 0,
|
|
12694
13950
|
describe: "Number of organization-owned sheets to skip"
|
|
13951
|
+
}).option("after-id", {
|
|
13952
|
+
type: "string",
|
|
13953
|
+
describe: "Return sheets with IDs after this keyset cursor; cannot be combined with --offset"
|
|
13954
|
+
}).option("all", {
|
|
13955
|
+
type: "boolean",
|
|
13956
|
+
default: false,
|
|
13957
|
+
describe: "Collect every organization-owned sheet in sequential pages of 100"
|
|
13958
|
+
}).option("result-file", {
|
|
13959
|
+
type: "string",
|
|
13960
|
+
nargs: 1,
|
|
13961
|
+
describe: "Publish the complete JSON result envelope to a new private file"
|
|
12695
13962
|
}).example("chalksurf --profile prod-codex sheet list --json", "List sheets owned by the selected organization").example(
|
|
13963
|
+
"chalksurf --profile prod-codex sheet list --all --result-file inventory.json --json",
|
|
13964
|
+
"Save a complete reviewed inventory"
|
|
13965
|
+
).example(
|
|
12696
13966
|
"chalksurf --profile prod-codex sheet list --folder-id 00000000-0000-4000-8000-000000000001 --include-descendants --json",
|
|
12697
13967
|
"List a folder subtree"
|
|
12698
13968
|
),
|
|
@@ -12705,8 +13975,21 @@ var registerSheetCommands = (sheetYargs, context) => {
|
|
|
12705
13975
|
if (includeDescendants && !folderId) {
|
|
12706
13976
|
throw new CliCommandError("--folder-id is required with --include-descendants.", 2);
|
|
12707
13977
|
}
|
|
12708
|
-
const
|
|
12709
|
-
const
|
|
13978
|
+
const collectAll = Boolean(argv.all);
|
|
13979
|
+
const afterId = resolveRequestedUuid2({
|
|
13980
|
+
flagValue: argv.afterId,
|
|
13981
|
+
label: "--after-id"
|
|
13982
|
+
});
|
|
13983
|
+
const limitArgument = argv.limit;
|
|
13984
|
+
const offsetArgument = argv.offset;
|
|
13985
|
+
if (collectAll && (limitArgument !== void 0 || offsetArgument !== void 0 || afterId !== void 0)) {
|
|
13986
|
+
throw new CliCommandError("--all cannot be combined with --limit, --offset, or --after-id.", 2);
|
|
13987
|
+
}
|
|
13988
|
+
if (afterId !== void 0 && offsetArgument !== void 0 && offsetArgument !== 0) {
|
|
13989
|
+
throw new CliCommandError("--after-id cannot be combined with a non-zero --offset.", 2);
|
|
13990
|
+
}
|
|
13991
|
+
const limit = collectAll ? null : normalizeLimit(limitArgument ?? 50);
|
|
13992
|
+
const offset = collectAll ? null : normalizeOffset(offsetArgument);
|
|
12710
13993
|
const { apiClient, organizationId } = await createResolvedApiClient({
|
|
12711
13994
|
context,
|
|
12712
13995
|
baseUrlFlagValue: argv.baseUrl,
|
|
@@ -12715,31 +13998,61 @@ var registerSheetCommands = (sheetYargs, context) => {
|
|
|
12715
13998
|
});
|
|
12716
13999
|
let result;
|
|
12717
14000
|
try {
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
|
|
12721
|
-
|
|
12722
|
-
|
|
12723
|
-
|
|
14001
|
+
if (collectAll) {
|
|
14002
|
+
const collected = await collectAllSheets({
|
|
14003
|
+
fetchPage: async ({ afterId: pageAfterId, limit: pageLimit }) => await apiClient.agentListSheets({
|
|
14004
|
+
afterId: pageAfterId,
|
|
14005
|
+
folderId,
|
|
14006
|
+
includeDescendants,
|
|
14007
|
+
limit: pageLimit,
|
|
14008
|
+
offset: 0
|
|
14009
|
+
})
|
|
14010
|
+
});
|
|
14011
|
+
for (const warning of collected.warnings) {
|
|
14012
|
+
context.output.info(warning);
|
|
14013
|
+
}
|
|
14014
|
+
result = {
|
|
14015
|
+
sheets: collected.sheets,
|
|
14016
|
+
totalCount: collected.totalCount,
|
|
14017
|
+
snapshot: collected.snapshot
|
|
14018
|
+
};
|
|
14019
|
+
} else {
|
|
14020
|
+
result = await apiClient.agentListSheets({
|
|
14021
|
+
afterId,
|
|
14022
|
+
folderId,
|
|
14023
|
+
includeDescendants,
|
|
14024
|
+
limit,
|
|
14025
|
+
offset
|
|
14026
|
+
});
|
|
14027
|
+
}
|
|
12724
14028
|
} catch (error) {
|
|
12725
14029
|
throw mapApiErrorToCliError(error);
|
|
12726
14030
|
}
|
|
12727
|
-
|
|
12728
|
-
{
|
|
12729
|
-
|
|
12730
|
-
|
|
12731
|
-
|
|
12732
|
-
|
|
12733
|
-
|
|
12734
|
-
|
|
12735
|
-
|
|
12736
|
-
...result
|
|
14031
|
+
const fullValue = {
|
|
14032
|
+
request: {
|
|
14033
|
+
...collectAll ? { all: true } : {},
|
|
14034
|
+
...afterId ? { afterId } : {},
|
|
14035
|
+
folderId: folderId ?? null,
|
|
14036
|
+
includeDescendants,
|
|
14037
|
+
limit,
|
|
14038
|
+
offset,
|
|
14039
|
+
organizationId: organizationId ?? null
|
|
12737
14040
|
},
|
|
12738
|
-
|
|
12739
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
12742
|
-
|
|
14041
|
+
...result
|
|
14042
|
+
};
|
|
14043
|
+
await printWithOptionalResultArtifact({
|
|
14044
|
+
command: "sheet list",
|
|
14045
|
+
compactHumanFormatter: ({ resultFile, sheetCount, snapshotComplete }) => `Saved ${sheetCount} sheet${sheetCount === 1 ? "" : "s"} to ${resultFile}.${snapshotComplete === false ? " Snapshot is incomplete." : ""}`,
|
|
14046
|
+
compactValue: {
|
|
14047
|
+
sheetCount: result.sheets.length,
|
|
14048
|
+
totalCount: result.totalCount,
|
|
14049
|
+
snapshotComplete: "snapshot" in result ? result.snapshot.complete : null
|
|
14050
|
+
},
|
|
14051
|
+
context,
|
|
14052
|
+
fullHumanFormatter: (output) => formatSheetListOutput(output),
|
|
14053
|
+
fullValue,
|
|
14054
|
+
resultFileInput: argv.resultFile
|
|
14055
|
+
});
|
|
12743
14056
|
}
|
|
12744
14057
|
).command(
|
|
12745
14058
|
"search",
|
|
@@ -13731,58 +15044,35 @@ var registerSheetCommands = (sheetYargs, context) => {
|
|
|
13731
15044
|
);
|
|
13732
15045
|
}
|
|
13733
15046
|
).command(
|
|
13734
|
-
"bulk-update",
|
|
13735
|
-
"
|
|
13736
|
-
(bulkUpdateYargs) => bulkUpdateYargs.
|
|
13737
|
-
|
|
13738
|
-
|
|
13739
|
-
|
|
13740
|
-
|
|
13741
|
-
|
|
13742
|
-
|
|
13743
|
-
|
|
13744
|
-
|
|
13745
|
-
|
|
13746
|
-
|
|
13747
|
-
|
|
13748
|
-
|
|
13749
|
-
|
|
13750
|
-
).
|
|
13751
|
-
|
|
13752
|
-
"
|
|
15047
|
+
"bulk-update [subcommand]",
|
|
15048
|
+
"Bulk sheet update and recovery commands",
|
|
15049
|
+
(bulkUpdateYargs) => bulkUpdateYargs.command(
|
|
15050
|
+
"$0",
|
|
15051
|
+
"Dry-run or apply up to 100 sheet patches",
|
|
15052
|
+
(runYargs) => configureBulkJsonInputOptions(
|
|
15053
|
+
runYargs,
|
|
15054
|
+
"Inline JSON with mode, updates, and confirmationDigest for apply mode"
|
|
15055
|
+
).example(
|
|
15056
|
+
"chalksurf sheet bulk-update --input batch-001.json --result-file dry-run.json --json",
|
|
15057
|
+
"Validate a reviewed batch and save the complete result"
|
|
15058
|
+
).example(
|
|
15059
|
+
"cat batch-001.json | chalksurf sheet bulk-update --input - --json",
|
|
15060
|
+
"Validate a reviewed batch from stdin"
|
|
15061
|
+
).example('chalksurf sheet bulk-update --input-json "$BATCH_JSON" --json', "Validate an inline batch"),
|
|
15062
|
+
async (argv) => await runBulkUpdateCommand(argv, context)
|
|
15063
|
+
).command(
|
|
15064
|
+
"verify",
|
|
15065
|
+
"Verify current targets after an uncertain bulk apply response",
|
|
15066
|
+
(verifyYargs) => configureBulkJsonInputOptions(
|
|
15067
|
+
verifyYargs,
|
|
15068
|
+
"Inline JSON containing updates, or a prior dry-run/apply manifest"
|
|
15069
|
+
).example(
|
|
15070
|
+
"chalksurf sheet bulk-update verify --input batch-001.json --result-file verification.json --json",
|
|
15071
|
+
"Classify the current state of every target in a batch"
|
|
15072
|
+
),
|
|
15073
|
+
async (argv) => await runVerifyBulkSheetUpdatesCommand(argv, context)
|
|
13753
15074
|
),
|
|
13754
|
-
|
|
13755
|
-
const input = parseBulkUpdateSheetsAgentInput(
|
|
13756
|
-
await loadJsonObjectInput({
|
|
13757
|
-
cwd: context.cwd,
|
|
13758
|
-
inputJson: argv.inputJson,
|
|
13759
|
-
inputPath: argv.input,
|
|
13760
|
-
stdin: context.stdin
|
|
13761
|
-
})
|
|
13762
|
-
);
|
|
13763
|
-
const { apiClient, organizationId } = await createResolvedApiClient({
|
|
13764
|
-
context,
|
|
13765
|
-
baseUrlFlagValue: argv.baseUrl,
|
|
13766
|
-
organizationFlagValue: argv.organization,
|
|
13767
|
-
profileName: argv.profile
|
|
13768
|
-
});
|
|
13769
|
-
let result;
|
|
13770
|
-
try {
|
|
13771
|
-
result = await apiClient.agentBulkUpdateSheets(input);
|
|
13772
|
-
} catch (error) {
|
|
13773
|
-
throw mapApiErrorToCliError(error);
|
|
13774
|
-
}
|
|
13775
|
-
context.output.print(
|
|
13776
|
-
{
|
|
13777
|
-
request: {
|
|
13778
|
-
...input,
|
|
13779
|
-
organizationId: organizationId ?? null
|
|
13780
|
-
},
|
|
13781
|
-
...result
|
|
13782
|
-
},
|
|
13783
|
-
(output) => formatBulkUpdateSheetsOutput(output),
|
|
13784
|
-
{ command: "sheet bulk-update" }
|
|
13785
|
-
);
|
|
15075
|
+
() => {
|
|
13786
15076
|
}
|
|
13787
15077
|
).command(
|
|
13788
15078
|
"import [sources..]",
|
|
@@ -14172,84 +15462,6 @@ var registerSheetCommands = (sheetYargs, context) => {
|
|
|
14172
15462
|
).demandCommand(1).strict();
|
|
14173
15463
|
};
|
|
14174
15464
|
|
|
14175
|
-
// src/lib/output.ts
|
|
14176
|
-
var cliJsonSchemaVersion = "v1";
|
|
14177
|
-
var ensureTrailingNewline = (value) => {
|
|
14178
|
-
return value.endsWith("\n") ? value : `${value}
|
|
14179
|
-
`;
|
|
14180
|
-
};
|
|
14181
|
-
var writeLine = (writer, value) => {
|
|
14182
|
-
writer.write(ensureTrailingNewline(value));
|
|
14183
|
-
};
|
|
14184
|
-
var createOutput = ({
|
|
14185
|
-
json,
|
|
14186
|
-
stdout,
|
|
14187
|
-
stderr
|
|
14188
|
-
}) => {
|
|
14189
|
-
let warnings = [];
|
|
14190
|
-
const consumeWarnings = () => {
|
|
14191
|
-
const nextWarnings = warnings;
|
|
14192
|
-
warnings = [];
|
|
14193
|
-
return nextWarnings;
|
|
14194
|
-
};
|
|
14195
|
-
return {
|
|
14196
|
-
json,
|
|
14197
|
-
print: (value, humanFormatter, options) => {
|
|
14198
|
-
if (json) {
|
|
14199
|
-
writeLine(
|
|
14200
|
-
stdout,
|
|
14201
|
-
JSON.stringify(
|
|
14202
|
-
{
|
|
14203
|
-
schemaVersion: cliJsonSchemaVersion,
|
|
14204
|
-
command: options.command,
|
|
14205
|
-
ok: options.ok ?? true,
|
|
14206
|
-
result: value,
|
|
14207
|
-
...options.error ? { error: options.error } : {},
|
|
14208
|
-
warnings: consumeWarnings()
|
|
14209
|
-
},
|
|
14210
|
-
null,
|
|
14211
|
-
2
|
|
14212
|
-
)
|
|
14213
|
-
);
|
|
14214
|
-
return;
|
|
14215
|
-
}
|
|
14216
|
-
const message = typeof humanFormatter === "function" ? humanFormatter(value) : humanFormatter;
|
|
14217
|
-
writeLine(stdout, message);
|
|
14218
|
-
},
|
|
14219
|
-
printError: ({ command, error, result }) => {
|
|
14220
|
-
if (json) {
|
|
14221
|
-
writeLine(
|
|
14222
|
-
stdout,
|
|
14223
|
-
JSON.stringify(
|
|
14224
|
-
{
|
|
14225
|
-
schemaVersion: cliJsonSchemaVersion,
|
|
14226
|
-
command,
|
|
14227
|
-
ok: false,
|
|
14228
|
-
...result === void 0 ? {} : { result },
|
|
14229
|
-
error,
|
|
14230
|
-
warnings: consumeWarnings()
|
|
14231
|
-
},
|
|
14232
|
-
null,
|
|
14233
|
-
2
|
|
14234
|
-
)
|
|
14235
|
-
);
|
|
14236
|
-
return;
|
|
14237
|
-
}
|
|
14238
|
-
writeLine(stderr, formatSerializableCliErrorForHuman(error));
|
|
14239
|
-
},
|
|
14240
|
-
info: (message) => {
|
|
14241
|
-
if (json) {
|
|
14242
|
-
warnings.push(message);
|
|
14243
|
-
return;
|
|
14244
|
-
}
|
|
14245
|
-
writeLine(stderr, message);
|
|
14246
|
-
},
|
|
14247
|
-
error: (message) => {
|
|
14248
|
-
writeLine(stderr, message);
|
|
14249
|
-
}
|
|
14250
|
-
};
|
|
14251
|
-
};
|
|
14252
|
-
|
|
14253
15465
|
// src/lib/prompt-secret.ts
|
|
14254
15466
|
import { createInterface } from "node:readline/promises";
|
|
14255
15467
|
import { Writable } from "node:stream";
|
|
@@ -14287,11 +15499,13 @@ var createPromptSecret = ({
|
|
|
14287
15499
|
};
|
|
14288
15500
|
|
|
14289
15501
|
// src/bin/chalksurf.ts
|
|
15502
|
+
var getHelpWidth = (stdout) => stdout.columns === void 0 ? null : Math.min(120, Math.max(80, stdout.columns));
|
|
14290
15503
|
var packageJson = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8"));
|
|
14291
15504
|
var createCli = ({
|
|
14292
15505
|
cwd,
|
|
14293
15506
|
configPath,
|
|
14294
15507
|
env,
|
|
15508
|
+
helpWidth,
|
|
14295
15509
|
now,
|
|
14296
15510
|
output,
|
|
14297
15511
|
sleep,
|
|
@@ -14308,7 +15522,7 @@ var createCli = ({
|
|
|
14308
15522
|
sleep,
|
|
14309
15523
|
stdin
|
|
14310
15524
|
};
|
|
14311
|
-
return yargs().scriptName("chalksurf").usage("$0 <command> [options]").version(false).option("json", {
|
|
15525
|
+
return yargs().wrap(helpWidth).scriptName("chalksurf").usage("$0 <command> [options]").version(false).option("json", {
|
|
14312
15526
|
type: "boolean",
|
|
14313
15527
|
default: false,
|
|
14314
15528
|
describe: "Write machine-readable JSON to stdout"
|
|
@@ -14352,6 +15566,12 @@ var createCli = ({
|
|
|
14352
15566
|
(exerciseYargs) => registerExerciseCommands(exerciseYargs, commandContext),
|
|
14353
15567
|
() => {
|
|
14354
15568
|
}
|
|
15569
|
+
).command(
|
|
15570
|
+
"quality-issue <subcommand>",
|
|
15571
|
+
"Exercise quality issue review commands",
|
|
15572
|
+
(qualityIssueYargs) => registerQualityIssueCommands(qualityIssueYargs, commandContext),
|
|
15573
|
+
() => {
|
|
15574
|
+
}
|
|
14355
15575
|
).command(
|
|
14356
15576
|
"feedback <subcommand>",
|
|
14357
15577
|
"Feedback commands",
|
|
@@ -14391,7 +15611,7 @@ var parseCliWithCapturedOutput = async ({
|
|
|
14391
15611
|
argv,
|
|
14392
15612
|
stdout
|
|
14393
15613
|
}) => {
|
|
14394
|
-
await new Promise((
|
|
15614
|
+
await new Promise((resolve8, reject) => {
|
|
14395
15615
|
cli.parse(argv, (error, _parsedArgv, output) => {
|
|
14396
15616
|
if (output) {
|
|
14397
15617
|
stdout.write(output);
|
|
@@ -14400,14 +15620,24 @@ var parseCliWithCapturedOutput = async ({
|
|
|
14400
15620
|
reject(error);
|
|
14401
15621
|
return;
|
|
14402
15622
|
}
|
|
14403
|
-
|
|
15623
|
+
resolve8();
|
|
14404
15624
|
});
|
|
14405
15625
|
});
|
|
14406
15626
|
};
|
|
14407
15627
|
var hasFlag = (argv, flags) => {
|
|
14408
15628
|
return argv.some((argument) => flags.includes(argument));
|
|
14409
15629
|
};
|
|
14410
|
-
var topLevelCommands = /* @__PURE__ */ new Set([
|
|
15630
|
+
var topLevelCommands = /* @__PURE__ */ new Set([
|
|
15631
|
+
"auth",
|
|
15632
|
+
"org",
|
|
15633
|
+
"profile",
|
|
15634
|
+
"exercise",
|
|
15635
|
+
"quality-issue",
|
|
15636
|
+
"feedback",
|
|
15637
|
+
"figure",
|
|
15638
|
+
"sheet",
|
|
15639
|
+
"job"
|
|
15640
|
+
]);
|
|
14411
15641
|
var hasTopLevelCommand = (argv) => {
|
|
14412
15642
|
return argv.some((argument) => topLevelCommands.has(argument));
|
|
14413
15643
|
};
|
|
@@ -14417,7 +15647,17 @@ var resolveCommandName = (argv) => {
|
|
|
14417
15647
|
if (!topLevelCommands.has(argument)) {
|
|
14418
15648
|
continue;
|
|
14419
15649
|
}
|
|
14420
|
-
const
|
|
15650
|
+
const commandArguments = argv.slice(index + 1);
|
|
15651
|
+
const subcommandIndex = commandArguments.findIndex(
|
|
15652
|
+
(candidate) => candidate.length > 0 && !candidate.startsWith("-") && !candidate.startsWith("http")
|
|
15653
|
+
);
|
|
15654
|
+
const subcommand = commandArguments[subcommandIndex];
|
|
15655
|
+
if (argument === "sheet" && subcommand === "bulk-update") {
|
|
15656
|
+
const nestedSubcommand = commandArguments.slice(subcommandIndex + 1).find((candidate) => candidate.length > 0 && !candidate.startsWith("-"));
|
|
15657
|
+
if (nestedSubcommand === "verify") {
|
|
15658
|
+
return "sheet bulk-update verify";
|
|
15659
|
+
}
|
|
15660
|
+
}
|
|
14421
15661
|
return subcommand ? `${argument} ${subcommand}` : argument;
|
|
14422
15662
|
}
|
|
14423
15663
|
if (hasFlag(argv, ["--version", "-v"])) {
|
|
@@ -14431,7 +15671,7 @@ var runCli = async ({
|
|
|
14431
15671
|
configPath,
|
|
14432
15672
|
env = process.env,
|
|
14433
15673
|
nowImpl = Date.now,
|
|
14434
|
-
sleepImpl = (milliseconds) => new Promise((
|
|
15674
|
+
sleepImpl = (milliseconds) => new Promise((resolve8) => setTimeout(resolve8, milliseconds)),
|
|
14435
15675
|
stdin = process.stdin,
|
|
14436
15676
|
stdout = process.stdout,
|
|
14437
15677
|
stderr = process.stderr
|
|
@@ -14446,6 +15686,7 @@ var runCli = async ({
|
|
|
14446
15686
|
cwd,
|
|
14447
15687
|
configPath,
|
|
14448
15688
|
env,
|
|
15689
|
+
helpWidth: getHelpWidth(stdout),
|
|
14449
15690
|
now: nowImpl,
|
|
14450
15691
|
output,
|
|
14451
15692
|
sleep: sleepImpl,
|