@codazen/harmonica-mcp 0.25.1 → 0.25.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +28 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22602,6 +22602,7 @@ var LIFECYCLE_STATES = [
|
|
|
22602
22602
|
"archived",
|
|
22603
22603
|
"deprecated"
|
|
22604
22604
|
];
|
|
22605
|
+
var PR_LIFECYCLE_STATES = ["draft", "open"];
|
|
22605
22606
|
|
|
22606
22607
|
// ../../libs/harmonica-services/src/mcp/tools/beat-version-tools.ts
|
|
22607
22608
|
function registerBeatVersionTools(server, ctx, client) {
|
|
@@ -24937,7 +24938,7 @@ function registerRevisionLifecycleTools(server, ctx, client) {
|
|
|
24937
24938
|
"Transition a revision to a new lifecycle state (e.g., concept \u2192 plan, build \u2192 deploy \u2192 support).",
|
|
24938
24939
|
{
|
|
24939
24940
|
revisionId: external_exports.string().describe("The revision ID (e.g., rev-abc123)"),
|
|
24940
|
-
targetState: external_exports.enum(LIFECYCLE_STATES).describe("The target lifecycle state"),
|
|
24941
|
+
targetState: external_exports.enum([...LIFECYCLE_STATES, ...PR_LIFECYCLE_STATES]).describe("The target lifecycle state"),
|
|
24941
24942
|
reason: external_exports.string().optional().describe("Why this transition is being made"),
|
|
24942
24943
|
skipQualityCheck: external_exports.boolean().optional().describe("Skip the quality check guard (for reconciling already-shipped code)"),
|
|
24943
24944
|
reconcile: external_exports.boolean().optional().describe("Walk through all intermediate forward states automatically. Use for already-shipped work that needs state machine catch-up.")
|
|
@@ -25154,11 +25155,14 @@ function registerSnapshotTools(server, ctx, client) {
|
|
|
25154
25155
|
"",
|
|
25155
25156
|
...versionNote,
|
|
25156
25157
|
` Beats: ${snapshot.beats.length}`,
|
|
25158
|
+
` Beat Versions: ${snapshot.beatVersions?.length ?? 0}`,
|
|
25157
25159
|
` Proposals: ${snapshot.proposals.length}`,
|
|
25158
25160
|
` Notes: ${snapshot.notes.length}`,
|
|
25159
25161
|
` Tasks: ${snapshot.tasks.length}`,
|
|
25160
25162
|
` Submissions: ${snapshot.submissions.length}`,
|
|
25161
25163
|
` PromptLogs: ${snapshot.promptLogs.length}`,
|
|
25164
|
+
` Drops: ${snapshot.drops?.length ?? 0}`,
|
|
25165
|
+
` Deliverables: ${snapshot.deliverables?.length ?? 0}`,
|
|
25162
25166
|
"",
|
|
25163
25167
|
"Use import_project_snapshot with this file path to import into another environment."
|
|
25164
25168
|
];
|
|
@@ -25169,9 +25173,12 @@ function registerSnapshotTools(server, ctx, client) {
|
|
|
25169
25173
|
"import_project_snapshot",
|
|
25170
25174
|
"Import a project snapshot into the current environment. Accepts an S3 presigned URL (https:// only), a file path, or inline JSON. Blocked in production by default.",
|
|
25171
25175
|
{
|
|
25172
|
-
snapshot: external_exports.string().describe("S3 presigned URL (https://...), file path, or inline JSON string")
|
|
25176
|
+
snapshot: external_exports.string().describe("S3 presigned URL (https://...), file path, or inline JSON string"),
|
|
25177
|
+
targetTeamspaceId: external_exports.string().min(1).optional().describe(
|
|
25178
|
+
"Remap all teamspace references (Drops, Deliverables, Project record) to this teamspace ID in the target environment. Required when the source and target environments use different teamspace UUIDs. When omitted and the snapshot contains Drops or Deliverables, a warning is included in the import summary."
|
|
25179
|
+
)
|
|
25173
25180
|
},
|
|
25174
|
-
async ({ snapshot: snapshotInput }) => {
|
|
25181
|
+
async ({ snapshot: snapshotInput, targetTeamspaceId }) => {
|
|
25175
25182
|
const isProduction = process.env.NODE_ENV === "production";
|
|
25176
25183
|
const importAllowed = process.env.ALLOW_PROJECT_IMPORT === "true";
|
|
25177
25184
|
if (isProduction && !importAllowed) {
|
|
@@ -25184,13 +25191,13 @@ function registerSnapshotTools(server, ctx, client) {
|
|
|
25184
25191
|
};
|
|
25185
25192
|
}
|
|
25186
25193
|
const trimmed = snapshotInput.trim();
|
|
25187
|
-
const importOptions = { targetOrgId: ctx.orgId, targetUserId: ctx.user.userId };
|
|
25194
|
+
const importOptions = { targetOrgId: ctx.orgId, targetUserId: ctx.user.userId, targetTeamspaceId };
|
|
25188
25195
|
if (trimmed.startsWith("https://")) {
|
|
25189
25196
|
if (isPrivateHost(trimmed)) {
|
|
25190
25197
|
throw new Error("Snapshot URL must point to a public host \u2014 private, link-local, and loopback addresses are not permitted.");
|
|
25191
25198
|
}
|
|
25192
25199
|
const result2 = await client.importProjectSnapshotFromUrl(trimmed, importOptions);
|
|
25193
|
-
return { content: [{ type: "text", text: formatImportSummary(result2) }] };
|
|
25200
|
+
return { content: [{ type: "text", text: formatImportSummary(result2, targetTeamspaceId) }] };
|
|
25194
25201
|
}
|
|
25195
25202
|
if (trimmed.startsWith("http://")) {
|
|
25196
25203
|
throw new Error("Snapshot URL must use https:// \u2014 http:// is not permitted.");
|
|
@@ -25205,7 +25212,7 @@ function registerSnapshotTools(server, ctx, client) {
|
|
|
25205
25212
|
assertSnapshotShape(parsed, "");
|
|
25206
25213
|
const normalized = normalizeSnapshot(parsed);
|
|
25207
25214
|
const result = await client.importProjectSnapshot(normalized, importOptions);
|
|
25208
|
-
return { content: [{ type: "text", text: formatImportSummary(result) }] };
|
|
25215
|
+
return { content: [{ type: "text", text: formatImportSummary(result, targetTeamspaceId) }] };
|
|
25209
25216
|
}
|
|
25210
25217
|
);
|
|
25211
25218
|
}
|
|
@@ -25248,7 +25255,7 @@ async function resolveSnapshotInput(input) {
|
|
|
25248
25255
|
}
|
|
25249
25256
|
return trimmed;
|
|
25250
25257
|
}
|
|
25251
|
-
function formatImportSummary(result) {
|
|
25258
|
+
function formatImportSummary(result, targetTeamspaceId) {
|
|
25252
25259
|
const lines = [
|
|
25253
25260
|
`Imported project: ${result.projectId}`,
|
|
25254
25261
|
` Beats: ${result.counts.beats}`,
|
|
@@ -25259,12 +25266,24 @@ function formatImportSummary(result) {
|
|
|
25259
25266
|
` Notes: ${result.counts.notes}`,
|
|
25260
25267
|
` Tasks: ${result.counts.tasks}`,
|
|
25261
25268
|
` Submissions: ${result.counts.submissions}`,
|
|
25262
|
-
` PromptLogs: ${result.counts.promptLogs}
|
|
25269
|
+
` PromptLogs: ${result.counts.promptLogs}`,
|
|
25270
|
+
` Beat Versions: ${result.counts.beatVersions ?? 0}`,
|
|
25271
|
+
` Drops: ${result.counts.drops ?? 0}`,
|
|
25272
|
+
` Deliverables: ${result.counts.deliverables ?? 0}`
|
|
25263
25273
|
];
|
|
25264
25274
|
if (result.errors.length > 0) {
|
|
25265
25275
|
lines.push("", `Errors (${result.errors.length}):`);
|
|
25266
25276
|
result.errors.forEach((e) => lines.push(` - ${e}`));
|
|
25267
25277
|
}
|
|
25278
|
+
const hasTeamspaceData = result.projectTeamspaceId !== void 0 || (result.counts.drops ?? 0) + (result.counts.deliverables ?? 0) > 0;
|
|
25279
|
+
if (!targetTeamspaceId && hasTeamspaceData) {
|
|
25280
|
+
lines.push(
|
|
25281
|
+
"",
|
|
25282
|
+
"Warning: snapshot contains teamspace-scoped data but targetTeamspaceId was not provided.",
|
|
25283
|
+
"The project record and any Drops or Deliverables were imported with their original teamspaceId and may not appear under the correct teamspace in this environment.",
|
|
25284
|
+
"Re-run with targetTeamspaceId set to the target teamspace UUID to remap them."
|
|
25285
|
+
);
|
|
25286
|
+
}
|
|
25268
25287
|
return lines.join("\n");
|
|
25269
25288
|
}
|
|
25270
25289
|
function isPrivateHost(url) {
|
|
@@ -27038,7 +27057,7 @@ function loadConfig() {
|
|
|
27038
27057
|
};
|
|
27039
27058
|
}
|
|
27040
27059
|
async function main() {
|
|
27041
|
-
console.error(`[harmonica-mcp] v${"0.25.
|
|
27060
|
+
console.error(`[harmonica-mcp] v${"0.25.3"} starting\u2026`);
|
|
27042
27061
|
const config2 = loadConfig();
|
|
27043
27062
|
const client = createHttpClient({
|
|
27044
27063
|
apiBaseUrl: config2.apiBaseUrl,
|