@contentrain/mcp 3.2.0 → 3.3.0
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
CHANGED
|
@@ -96,7 +96,7 @@ All write operations are designed around git-backed safety:
|
|
|
96
96
|
| `contentrain_branch_delete` | Delete a stale/failed `cr/*` branch locally and on the remote (contentrain branch protected) | — | **Yes** |
|
|
97
97
|
| `contentrain_scan` | Graph- and candidate-based hardcoded string scan | Yes | — |
|
|
98
98
|
| `contentrain_apply` | Normalize extract/reuse execution with dry-run support | — | — |
|
|
99
|
-
| `contentrain_bulk` | Bulk locale copy, status updates, and deletes | — | — |
|
|
99
|
+
| `contentrain_bulk` | Bulk locale copy, status updates, and deletes (`dry_run` previews) | — | — |
|
|
100
100
|
| `contentrain_media_list` | List media assets (search, tag filter, cursor pagination) | Yes | — |
|
|
101
101
|
| `contentrain_media_get` | Get one media asset by id | Yes | — |
|
|
102
102
|
| `contentrain_media_ingest` | Ingest an asset from a source URL (provider fetches server-side) | — | — |
|
package/dist/index.mjs
CHANGED
|
@@ -15,7 +15,7 @@ import "./transaction-BR2jg4uj.mjs";
|
|
|
15
15
|
import "./local-DbPlR0Br.mjs";
|
|
16
16
|
import "./detect-CI32ynpq.mjs";
|
|
17
17
|
import "./annotations-CXHPnS-M.mjs";
|
|
18
|
-
import { n as createServer } from "./server-
|
|
18
|
+
import { n as createServer } from "./server-CFjIoTWt.mjs";
|
|
19
19
|
import "./errors-BmLE-JSC.mjs";
|
|
20
20
|
import "./reconcile-EANDIp_T.mjs";
|
|
21
21
|
import "./scan-config-C5zzygeH.mjs";
|
|
@@ -14,7 +14,7 @@ import "../../transaction-BR2jg4uj.mjs";
|
|
|
14
14
|
import "../../local-DbPlR0Br.mjs";
|
|
15
15
|
import "../../detect-CI32ynpq.mjs";
|
|
16
16
|
import "../../annotations-CXHPnS-M.mjs";
|
|
17
|
-
import { n as createServer } from "../../server-
|
|
17
|
+
import { n as createServer } from "../../server-CFjIoTWt.mjs";
|
|
18
18
|
import "../../errors-BmLE-JSC.mjs";
|
|
19
19
|
import "../../reconcile-EANDIp_T.mjs";
|
|
20
20
|
import "../../scan-config-C5zzygeH.mjs";
|
|
@@ -2412,7 +2412,7 @@ function registerNormalizeTools(server, provider, projectRoot) {
|
|
|
2412
2412
|
//#endregion
|
|
2413
2413
|
//#region src/tools/bulk.ts
|
|
2414
2414
|
function registerBulkTools(server, _provider, projectRoot) {
|
|
2415
|
-
server.tool("contentrain_bulk", "Batch operations on content entries. All operations are auto-committed to git.", {
|
|
2415
|
+
server.tool("contentrain_bulk", "Batch operations on content entries. All operations are auto-committed to git. DRY RUN (dry_run:true): runs the whole operation in a throwaway worktree and reports exactly what would change — nothing is committed and no branch survives. Recommended workflow: preview with dry_run:true, review the counts, then call again to execute.", {
|
|
2416
2416
|
operation: z.enum([
|
|
2417
2417
|
"copy_locale",
|
|
2418
2418
|
"update_status",
|
|
@@ -2431,7 +2431,8 @@ function registerBulkTools(server, _provider, projectRoot) {
|
|
|
2431
2431
|
"rejected",
|
|
2432
2432
|
"archived"
|
|
2433
2433
|
]).optional().describe("New status for update_status"),
|
|
2434
|
-
confirm: z.boolean().optional().describe("Must be true for delete_entries")
|
|
2434
|
+
confirm: z.boolean().optional().describe("Must be true for delete_entries (not needed while dry_run:true — a preview deletes nothing)"),
|
|
2435
|
+
dry_run: z.boolean().optional().describe("Preview mode: perform the operation in a throwaway worktree and report what would change, committing nothing. Defaults to false so existing callers keep executing.")
|
|
2435
2436
|
}, TOOL_ANNOTATIONS["contentrain_bulk"], async (input) => {
|
|
2436
2437
|
if (!projectRoot) return capabilityError("contentrain_bulk", "localWorktree");
|
|
2437
2438
|
const config = await readConfig(projectRoot);
|
|
@@ -2503,12 +2504,15 @@ function registerBulkTools(server, _provider, projectRoot) {
|
|
|
2503
2504
|
const tx = await createTransaction(projectRoot, branch);
|
|
2504
2505
|
try {
|
|
2505
2506
|
let copiedCount = 0;
|
|
2507
|
+
let replacedCount = 0;
|
|
2506
2508
|
await tx.write(async (wt) => {
|
|
2507
2509
|
const cDir = resolveContentDir(wt, model);
|
|
2508
2510
|
const sourceFile = resolveJsonFilePath(cDir, model, input.source_locale);
|
|
2509
2511
|
const targetFile = resolveJsonFilePath(cDir, model, input.target_locale);
|
|
2510
2512
|
const sourceData = await readJson(sourceFile);
|
|
2511
2513
|
if (!sourceData) throw new Error(`No content found for locale "${input.source_locale}" in model "${input.model}"`);
|
|
2514
|
+
const targetData = await readJson(targetFile);
|
|
2515
|
+
if (targetData) replacedCount = model.kind === "collection" ? Object.keys(targetData).length : 1;
|
|
2512
2516
|
await writeJson(targetFile, sourceData);
|
|
2513
2517
|
if (model.kind === "collection") {
|
|
2514
2518
|
const entries = sourceData;
|
|
@@ -2526,6 +2530,21 @@ function registerBulkTools(server, _provider, projectRoot) {
|
|
|
2526
2530
|
copiedCount = 1;
|
|
2527
2531
|
}
|
|
2528
2532
|
});
|
|
2533
|
+
if (input.dry_run) {
|
|
2534
|
+
await tx.cleanup();
|
|
2535
|
+
return { content: [{
|
|
2536
|
+
type: "text",
|
|
2537
|
+
text: JSON.stringify({
|
|
2538
|
+
status: "preview",
|
|
2539
|
+
dry_run: true,
|
|
2540
|
+
operation: "copy_locale",
|
|
2541
|
+
message: `Would copy ${copiedCount} entries from ${input.source_locale} to ${input.target_locale}.`,
|
|
2542
|
+
would_copy: copiedCount,
|
|
2543
|
+
would_replace: replacedCount,
|
|
2544
|
+
next_steps: replacedCount > 0 ? [`${replacedCount} record(s) already exist in "${input.target_locale}" and would be REPLACED — confirm that is intended`, "Call contentrain_bulk again without dry_run to execute"] : ["Call contentrain_bulk again without dry_run to execute"]
|
|
2545
|
+
}, null, 2)
|
|
2546
|
+
}] };
|
|
2547
|
+
}
|
|
2529
2548
|
await tx.commit(`[contentrain] bulk: copy ${input.source_locale} → ${input.target_locale} for ${input.model}`, {
|
|
2530
2549
|
tool: "contentrain_bulk",
|
|
2531
2550
|
model: input.model,
|
|
@@ -2690,6 +2709,22 @@ function registerBulkTools(server, _provider, projectRoot) {
|
|
|
2690
2709
|
isError: true
|
|
2691
2710
|
};
|
|
2692
2711
|
}
|
|
2712
|
+
if (input.dry_run) {
|
|
2713
|
+
await tx.cleanup();
|
|
2714
|
+
return { content: [{
|
|
2715
|
+
type: "text",
|
|
2716
|
+
text: JSON.stringify({
|
|
2717
|
+
status: "preview",
|
|
2718
|
+
dry_run: true,
|
|
2719
|
+
operation: "update_status",
|
|
2720
|
+
message: `Would update ${updatedCount} meta record(s) to status "${input.status}" across locales [${Object.keys(updatedPerLocale).join(", ")}].`,
|
|
2721
|
+
would_update: updatedCount,
|
|
2722
|
+
would_update_by_locale: updatedPerLocale,
|
|
2723
|
+
not_found: notFound.length > 0 ? notFound : void 0,
|
|
2724
|
+
next_steps: ["Call contentrain_bulk again without dry_run to execute"]
|
|
2725
|
+
}, null, 2)
|
|
2726
|
+
}] };
|
|
2727
|
+
}
|
|
2693
2728
|
await tx.commit(`[contentrain] bulk: update status → ${input.status} for ${input.model}`, {
|
|
2694
2729
|
tool: "contentrain_bulk",
|
|
2695
2730
|
model: input.model,
|
|
@@ -2739,10 +2774,13 @@ function registerBulkTools(server, _provider, projectRoot) {
|
|
|
2739
2774
|
}],
|
|
2740
2775
|
isError: true
|
|
2741
2776
|
};
|
|
2742
|
-
if (input.confirm !== true) return {
|
|
2777
|
+
if (!input.dry_run && input.confirm !== true) return {
|
|
2743
2778
|
content: [{
|
|
2744
2779
|
type: "text",
|
|
2745
|
-
text: JSON.stringify({
|
|
2780
|
+
text: JSON.stringify({
|
|
2781
|
+
error: "delete_entries requires confirm:true",
|
|
2782
|
+
hint: "Run with dry_run:true first to see which files would be removed — no confirmation needed for a preview."
|
|
2783
|
+
})
|
|
2746
2784
|
}],
|
|
2747
2785
|
isError: true
|
|
2748
2786
|
};
|
|
@@ -2766,6 +2804,21 @@ function registerBulkTools(server, _provider, projectRoot) {
|
|
|
2766
2804
|
allRemoved.push(...removed);
|
|
2767
2805
|
}
|
|
2768
2806
|
});
|
|
2807
|
+
if (input.dry_run) {
|
|
2808
|
+
await tx.cleanup();
|
|
2809
|
+
return { content: [{
|
|
2810
|
+
type: "text",
|
|
2811
|
+
text: JSON.stringify({
|
|
2812
|
+
status: "preview",
|
|
2813
|
+
dry_run: true,
|
|
2814
|
+
operation: "delete_entries",
|
|
2815
|
+
message: `Would delete ${input.entry_ids.length} entries, removing ${allRemoved.length} file(s).`,
|
|
2816
|
+
would_delete: input.entry_ids.length,
|
|
2817
|
+
files_to_remove: allRemoved,
|
|
2818
|
+
next_steps: ["Call contentrain_bulk again with dry_run:false and confirm:true to execute"]
|
|
2819
|
+
}, null, 2)
|
|
2820
|
+
}] };
|
|
2821
|
+
}
|
|
2769
2822
|
await tx.commit(`[contentrain] bulk: delete ${input.entry_ids.length} entries from ${input.model}`, {
|
|
2770
2823
|
tool: "contentrain_bulk",
|
|
2771
2824
|
model: input.model,
|
|
@@ -2959,7 +3012,7 @@ function registerMediaTools(server, provider, _projectRoot) {
|
|
|
2959
3012
|
}
|
|
2960
3013
|
//#endregion
|
|
2961
3014
|
//#region package.json
|
|
2962
|
-
var version = "3.
|
|
3015
|
+
var version = "3.3.0";
|
|
2963
3016
|
//#endregion
|
|
2964
3017
|
//#region src/server.ts
|
|
2965
3018
|
/**
|
|
@@ -3034,4 +3087,4 @@ function resolveServerContext(input) {
|
|
|
3034
3087
|
//#endregion
|
|
3035
3088
|
export { createServer as n, DEFAULT_INSTRUCTIONS as t };
|
|
3036
3089
|
|
|
3037
|
-
//# sourceMappingURL=server-
|
|
3090
|
+
//# sourceMappingURL=server-CFjIoTWt.mjs.map
|