draftgo-cli 3.0.48 → 3.0.49

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.
@@ -0,0 +1,122 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const { loadProjectConfig } = require('../projectConfig');
5
+ const { DraftGoMcpClient } = require('../mcp/client');
6
+ const backendDefaults = require('./backend');
7
+ const { loadManifest, absolutePath } = require('./manifest');
8
+ const { hashFile } = require('./streams');
9
+
10
+ function versionValue(source) {
11
+ if (!source) return null;
12
+ if (source.base_version != null) return source.base_version;
13
+ if (source.base_revision != null) return source.base_revision;
14
+ return null;
15
+ }
16
+
17
+ function sameVersion(entry, remote) {
18
+ const local = versionValue(entry);
19
+ const current = versionValue(remote);
20
+ if (local == null || current == null) return true;
21
+ return String(local) === String(current);
22
+ }
23
+
24
+ async function optionalHash(projectDir, relative) {
25
+ if (!relative) return null;
26
+ const file = absolutePath(projectDir, relative);
27
+ return fs.existsSync(file) ? (await hashFile(file)).hash : null;
28
+ }
29
+
30
+ function classify(entry, localHash, baseHash, remote) {
31
+ const manifestHash = entry.base_hash;
32
+ const remoteHash = remote && remote.content_hash || null;
33
+ const baseMatchesManifest = baseHash === manifestHash;
34
+ const localMatchesManifest = localHash === manifestHash;
35
+ const localMatchesRemote = Boolean(localHash && remoteHash && localHash === remoteHash);
36
+ const remoteMatchesManifest = Boolean(remoteHash && remoteHash === manifestHash);
37
+ const versionMatches = remote ? sameVersion(entry, remote) : null;
38
+
39
+ let state;
40
+ let recommendation = null;
41
+ if (!localHash) {
42
+ state = 'local_missing';
43
+ recommendation = `draftgo checkout ${entry.resource_type} ${entry.resource_id} --force`;
44
+ } else if (!baseHash) {
45
+ state = 'base_missing';
46
+ recommendation = `draftgo checkout ${entry.resource_type} ${entry.resource_id} --force`;
47
+ } else if (!remote) {
48
+ state = baseMatchesManifest
49
+ ? (localMatchesManifest ? 'clean_local' : 'local_modified')
50
+ : 'local_metadata_corrupt';
51
+ } else if (localMatchesRemote && (!remoteMatchesManifest || !versionMatches || !baseMatchesManifest)) {
52
+ state = remoteMatchesManifest ? 'metadata_stale' : 'committed_unrecorded';
53
+ recommendation = `draftgo reconcile ${entry.resource_type} ${entry.resource_id}`;
54
+ } else if (!baseMatchesManifest) {
55
+ state = 'local_metadata_corrupt';
56
+ recommendation = `draftgo checkout ${entry.resource_type} ${entry.resource_id} --force`;
57
+ } else if (remoteMatchesManifest && versionMatches) {
58
+ state = localMatchesManifest ? 'clean' : 'local_modified';
59
+ } else if (localMatchesManifest) {
60
+ state = 'remote_changed';
61
+ recommendation = `draftgo checkout ${entry.resource_type} ${entry.resource_id}`;
62
+ } else {
63
+ state = 'diverged';
64
+ recommendation = `draftgo commit ${entry.resource_type} ${entry.resource_id}`;
65
+ }
66
+
67
+ return {
68
+ state,
69
+ local_hash: localHash,
70
+ base_file_hash: baseHash,
71
+ manifest_hash: manifestHash,
72
+ remote_hash: remoteHash,
73
+ manifest_version: versionValue(entry),
74
+ remote_version: versionValue(remote),
75
+ version_matches: versionMatches,
76
+ local_matches_remote: localMatchesRemote,
77
+ recommendation,
78
+ };
79
+ }
80
+
81
+ async function inspectEntry(projectDir, entry, remote = null) {
82
+ const [localHash, baseHash] = await Promise.all([
83
+ optionalHash(projectDir, entry.local_path),
84
+ optionalHash(projectDir, entry.base_path),
85
+ ]);
86
+ return { ...entry, ...classify(entry, localHash, baseHash, remote) };
87
+ }
88
+
89
+ async function openMetadataSession(config, options = {}) {
90
+ if (options.client && options.tools) return { client: options.client, tools: options.tools };
91
+ const client = options.client || new DraftGoMcpClient(config);
92
+ await client.initialize(options);
93
+ const tools = options.tools || await client.listAllTools(options);
94
+ return { client, tools };
95
+ }
96
+
97
+ async function inspectRemoteCheckouts(projectDir, options = {}) {
98
+ const config = options.config || loadProjectConfig(projectDir);
99
+ const manifest = options.manifest || loadManifest(projectDir);
100
+ const backend = { ...backendDefaults, ...(options.backend || {}) };
101
+ const session = options.backend && typeof options.backend.resolveMetadata === 'function'
102
+ ? { client: options.client || {}, tools: options.tools || [] }
103
+ : await openMetadataSession(config, options);
104
+ const entries = Object.values(manifest.entries);
105
+ return Promise.all(entries.map(async (entry) => {
106
+ const remote = await backend.resolveMetadata(config, entry.resource_type, entry.resource_id, {
107
+ ...options,
108
+ ...session,
109
+ clientInitialized: true,
110
+ });
111
+ return inspectEntry(projectDir, entry, remote);
112
+ }));
113
+ }
114
+
115
+ module.exports = {
116
+ versionValue,
117
+ sameVersion,
118
+ classify,
119
+ inspectEntry,
120
+ openMetadataSession,
121
+ inspectRemoteCheckouts,
122
+ };