sysprom 1.3.0 → 1.5.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/dist/src/cli/commands/init.d.ts +1 -1
- package/dist/src/cli/commands/update.js +2 -2
- package/dist/src/operations/index.d.ts +1 -0
- package/dist/src/operations/index.js +2 -0
- package/dist/src/operations/remove-node.d.ts +3 -0
- package/dist/src/operations/remove-node.js +58 -10
- package/dist/src/operations/remove-relationship.d.ts +239 -234
- package/dist/src/operations/remove-relationship.js +56 -5
- package/dist/src/operations/sync.d.ts +945 -0
- package/dist/src/operations/sync.js +123 -0
- package/dist/src/sync.d.ts +26 -0
- package/dist/src/sync.js +81 -0
- package/package.json +1 -1
|
@@ -7,9 +7,9 @@ declare const optsSchema: z.ZodObject<{
|
|
|
7
7
|
title: z.ZodOptional<z.ZodString>;
|
|
8
8
|
scope: z.ZodOptional<z.ZodString>;
|
|
9
9
|
format: z.ZodOptional<z.ZodEnum<{
|
|
10
|
-
dir: "dir";
|
|
11
10
|
json: "json";
|
|
12
11
|
md: "md";
|
|
12
|
+
dir: "dir";
|
|
13
13
|
}>>;
|
|
14
14
|
}, z.core.$strict>;
|
|
15
15
|
export declare const initCommand: CommandDef<typeof argsSchema, typeof optsSchema>;
|
|
@@ -147,13 +147,13 @@ const removeRelSubcommand = {
|
|
|
147
147
|
const opts = removeRelOpts.parse(rawOpts);
|
|
148
148
|
const loaded = loadDoc(opts.path);
|
|
149
149
|
const { doc } = loaded;
|
|
150
|
-
const
|
|
150
|
+
const result = removeRelationshipOp({
|
|
151
151
|
doc,
|
|
152
152
|
from: args.from,
|
|
153
153
|
type: args.type,
|
|
154
154
|
to: args.to,
|
|
155
155
|
});
|
|
156
|
-
persistDoc(
|
|
156
|
+
persistDoc(result.doc, loaded, opts);
|
|
157
157
|
if (opts.json) {
|
|
158
158
|
console.log(JSON.stringify({ from: args.from, type: args.type, to: args.to }, null, 2));
|
|
159
159
|
}
|
|
@@ -32,6 +32,7 @@ export { graphOp } from "./graph.js";
|
|
|
32
32
|
export { renameOp } from "./rename.js";
|
|
33
33
|
export { jsonToMarkdownOp } from "./json-to-markdown.js";
|
|
34
34
|
export { markdownToJsonOp } from "./markdown-to-json.js";
|
|
35
|
+
export { syncDocumentsOp, type BidirectionalSyncResult, type ConflictStrategy, } from "./sync.js";
|
|
35
36
|
export { speckitImportOp } from "./speckit-import.js";
|
|
36
37
|
export { speckitExportOp } from "./speckit-export.js";
|
|
37
38
|
export { speckitSyncOp, type SyncResult } from "./speckit-sync.js";
|
|
@@ -38,6 +38,8 @@ export { renameOp } from "./rename.js";
|
|
|
38
38
|
// Conversion operations
|
|
39
39
|
export { jsonToMarkdownOp } from "./json-to-markdown.js";
|
|
40
40
|
export { markdownToJsonOp } from "./markdown-to-json.js";
|
|
41
|
+
// Synchronisation operations
|
|
42
|
+
export { syncDocumentsOp, } from "./sync.js";
|
|
41
43
|
// Spec-Kit interoperability operations
|
|
42
44
|
export { speckitImportOp } from "./speckit-import.js";
|
|
43
45
|
export { speckitExportOp } from "./speckit-export.js";
|
|
@@ -602,6 +602,9 @@ export declare const removeNodeOp: import("./define-operation.js").DefinedOperat
|
|
|
602
602
|
};
|
|
603
603
|
};
|
|
604
604
|
id: z.ZodString;
|
|
605
|
+
hard: z.ZodOptional<z.ZodBoolean>;
|
|
606
|
+
recursive: z.ZodOptional<z.ZodBoolean>;
|
|
607
|
+
repair: z.ZodOptional<z.ZodBoolean>;
|
|
605
608
|
}, z.core.$strip>, z.ZodObject<{
|
|
606
609
|
doc: z.ZodObject<{
|
|
607
610
|
$schema: z.ZodOptional<z.ZodString>;
|
|
@@ -17,17 +17,71 @@ export const removeNodeOp = defineOperation({
|
|
|
17
17
|
input: z.object({
|
|
18
18
|
doc: SysProMDocument,
|
|
19
19
|
id: z.string(),
|
|
20
|
+
hard: z.boolean().optional(),
|
|
21
|
+
recursive: z.boolean().optional(),
|
|
22
|
+
repair: z.boolean().optional(),
|
|
20
23
|
}),
|
|
21
24
|
output: RemoveResult,
|
|
22
|
-
fn({ doc, id }) {
|
|
25
|
+
fn({ doc, id, hard, recursive, repair }) {
|
|
23
26
|
const nodeIdx = doc.nodes.findIndex((n) => n.id === id);
|
|
24
27
|
if (nodeIdx === -1) {
|
|
25
28
|
throw new Error(`Node not found: ${id}`);
|
|
26
29
|
}
|
|
30
|
+
const nodeToRemove = doc.nodes[nodeIdx];
|
|
27
31
|
const warnings = [];
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
// Check recursive guard for hard delete
|
|
33
|
+
if (hard && nodeToRemove.subsystem) {
|
|
34
|
+
if (!recursive) {
|
|
35
|
+
throw new Error(`Cannot hard delete node ${id} with subsystem without --recursive flag`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
let newNodes;
|
|
39
|
+
let newRelationships = doc.relationships ?? [];
|
|
40
|
+
if (hard) {
|
|
41
|
+
// Hard delete: physically remove the node
|
|
42
|
+
newNodes = doc.nodes.filter((n) => n.id !== id);
|
|
43
|
+
// Handle must_follow chain repair if requested
|
|
44
|
+
if (repair) {
|
|
45
|
+
const incomingChains = newRelationships.filter((r) => r.to === id && r.type === "must_follow");
|
|
46
|
+
const outgoingChains = newRelationships.filter((r) => r.from === id && r.type === "must_follow");
|
|
47
|
+
// Remove all relationships involving the deleted node
|
|
48
|
+
newRelationships = newRelationships.filter((r) => r.from !== id && r.to !== id);
|
|
49
|
+
// Repair chains by connecting incoming to outgoing
|
|
50
|
+
// Only repair if there are both incoming AND outgoing chains
|
|
51
|
+
if (incomingChains.length > 0 && outgoingChains.length > 0) {
|
|
52
|
+
for (const incoming of incomingChains) {
|
|
53
|
+
for (const outgoing of outgoingChains) {
|
|
54
|
+
// Only add if not already connected
|
|
55
|
+
const exists = newRelationships.some((r) => r.from === incoming.from &&
|
|
56
|
+
r.to === outgoing.to &&
|
|
57
|
+
r.type === "must_follow");
|
|
58
|
+
if (!exists) {
|
|
59
|
+
newRelationships.push({
|
|
60
|
+
from: incoming.from,
|
|
61
|
+
to: outgoing.to,
|
|
62
|
+
type: "must_follow",
|
|
63
|
+
});
|
|
64
|
+
warnings.push(`Repaired chain: ${incoming.from} → ${outgoing.to}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// Without repair, just remove all relationships
|
|
72
|
+
const oldRelCount = newRelationships.length;
|
|
73
|
+
newRelationships = newRelationships.filter((r) => r.from !== id && r.to !== id);
|
|
74
|
+
if (newRelationships.length < oldRelCount) {
|
|
75
|
+
warnings.push(`Removed relationships involving ${id}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// Soft delete: mark as retired and preserve relationships
|
|
81
|
+
newNodes = doc.nodes.map((n) => n.id === id ? { ...n, status: "retired" } : n);
|
|
82
|
+
// Don't remove relationships in soft delete
|
|
83
|
+
}
|
|
84
|
+
// Clean up all references to the removed node (both soft and hard)
|
|
31
85
|
const cleanedNodes = newNodes.map((n) => {
|
|
32
86
|
let updated = n;
|
|
33
87
|
// Remove from view includes
|
|
@@ -59,12 +113,6 @@ export const removeNodeOp = defineOperation({
|
|
|
59
113
|
}
|
|
60
114
|
return updated;
|
|
61
115
|
});
|
|
62
|
-
// Remove relationships involving this node
|
|
63
|
-
const oldRelCount = (doc.relationships ?? []).length;
|
|
64
|
-
const newRelationships = (doc.relationships ?? []).filter((r) => r.from !== id && r.to !== id);
|
|
65
|
-
if (newRelationships.length < oldRelCount) {
|
|
66
|
-
warnings.push(`Removed relationships involving ${id}`);
|
|
67
|
-
}
|
|
68
116
|
// Remove from external references
|
|
69
117
|
const newExternalRefs = (doc.external_references ?? []).filter((ref) => ref.node_id !== id);
|
|
70
118
|
return {
|