draftgo-cli 4.0.25 → 4.0.26
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 +21 -37
- package/package.json +3 -5
- package/resources/skill/SKILL.md +9 -5
- package/resources/skill/manifest.json +2 -5
- package/resources/skill/references/ai.md +41 -0
- package/resources/skill/references/app-api.md +2 -50
- package/resources/skill/references/architecture.md +1 -1
- package/resources/skill/references/chat-sdk.md +29 -37
- package/resources/skill/references/checkout.md +4 -4
- package/resources/skill/references/data.md +0 -46
- package/resources/skill/references/delivery.md +3 -3
- package/resources/skill/references/diagnostics.md +10 -11
- package/resources/skill/references/frontend.md +23 -20
- package/resources/skill/references/mcp.md +4 -14
- package/resources/skill/references/methods.md +15 -68
- package/resources/skill/references/modules.md +23 -44
- package/resources/skill/references/runtime.md +3 -20
- package/resources/skill/story/SKILL.md +2 -2
- package/src/apiContractCache.js +14 -6
- package/src/cli.js +0 -7
- package/src/commandRegistry.js +0 -6
- package/src/commands/api.js +87 -17
- package/src/commands/apiKey.js +2 -6
- package/src/commands/autoPush.js +15 -51
- package/src/commands/capabilities.js +22 -15
- package/src/commands/check.js +19 -53
- package/src/commands/checkout.js +1 -4
- package/src/commands/clean.js +1 -1
- package/src/commands/commit.js +1 -4
- package/src/commands/components.js +12 -8
- package/src/commands/conflict.js +4 -6
- package/src/commands/conflicts.js +1 -2
- package/src/commands/connect.js +0 -8
- package/src/commands/delete.js +15 -11
- package/src/commands/deploy.js +64 -26
- package/src/commands/diff.js +1 -4
- package/src/commands/group.js +2 -3
- package/src/commands/help.js +19 -41
- package/src/commands/init.js +13 -6
- package/src/commands/local.js +4 -1
- package/src/commands/map.js +138 -23
- package/src/commands/reconcile.js +1 -15
- package/src/commands/role.js +1 -2
- package/src/commands/status.js +12 -40
- package/src/commands/verify.js +8 -7
- package/src/commands/worklog.js +11 -5
- package/src/contractCompatibility.js +10 -2
- package/src/localRuntime/compose.js +41 -27
- package/src/localRuntime/detect.js +6 -6
- package/src/localRuntime/index.js +47 -47
- package/src/localRuntime/services.js +2 -39
- package/src/mcp/client.js +99 -134
- package/src/mcp/parallel.js +25 -2
- package/src/mcp/protocol.js +38 -9
- package/src/mcp/tools.js +10 -19
- package/src/projectConfig.js +1 -4
- package/src/{workspaceHealth.js → projectHealth.js} +5 -5
- package/src/projectMap.js +1 -1
- package/src/runtimeFiles.js +2 -1
- package/src/worklog.js +3 -2
- package/src/worktree/backend.js +127 -15
- package/src/worktree/index.js +64 -22
- package/src/worktree/locks.js +52 -0
- package/src/worktree/manifest.js +18 -4
- package/src/worktree/status.js +4 -2
- package/resources/custom-service-sdk/ai.go +0 -520
- package/resources/custom-service-sdk/ai_test.go +0 -156
- package/resources/custom-service-sdk/auth_test.go +0 -56
- package/resources/custom-service-sdk/billing.go +0 -596
- package/resources/custom-service-sdk/billing_test.go +0 -150
- package/resources/custom-service-sdk/go.mod +0 -3
- package/resources/custom-service-sdk/manifest.json +0 -77
- package/resources/custom-service-sdk/platform.go +0 -352
- package/resources/custom-service-sdk/platform_logger_test.go +0 -24
- package/resources/custom-service-sdk/registration_test.go +0 -39
- package/resources/custom-service-sdk/resources.go +0 -247
- package/resources/custom-service-sdk/resources_billing_test.go +0 -115
- package/resources/custom-service-sdk/resources_files_test.go +0 -57
- package/resources/custom-service-sdk/resources_scope_test.go +0 -92
- package/resources/custom-service-sdk/sdk.go +0 -209
- package/resources/skill/references/aihub.md +0 -116
- package/resources/skill/references/custom-services.md +0 -201
- package/src/commands/customService.js +0 -95
- package/src/commands/dataRange.js +0 -33
- package/src/commands/grant.js +0 -29
- package/src/commands/space.js +0 -41
- package/src/customServices.js +0 -484
package/src/worktree/index.js
CHANGED
|
@@ -33,6 +33,8 @@ const {
|
|
|
33
33
|
const { validateContentFile } = require('./validate');
|
|
34
34
|
const { inspectEntry, openMetadataSession, sameVersion } = require('./status');
|
|
35
35
|
|
|
36
|
+
const { withResourceLocks } = require('./locks');
|
|
37
|
+
const { mapBounded } = require('../mcp/parallel');
|
|
36
38
|
const CONFLICT_SCHEMA_VERSION = 1;
|
|
37
39
|
|
|
38
40
|
function backendFor(options) {
|
|
@@ -93,17 +95,18 @@ function assertMetadataIdentity(metadata, resourceType, resourceId) {
|
|
|
93
95
|
}
|
|
94
96
|
}
|
|
95
97
|
|
|
96
|
-
async function
|
|
98
|
+
async function checkoutResourcesUnlocked(projectDir, resourceType, resourceIds, options = {}) {
|
|
97
99
|
const canonical = canonicalResourceType(resourceType);
|
|
98
100
|
const ids = ensureIds(resourceIds);
|
|
99
101
|
const config = options.config || loadProjectConfig(projectDir);
|
|
100
102
|
const backend = backendFor(options);
|
|
101
103
|
const session = await createSession(config, options);
|
|
102
104
|
const manifest = loadManifest(projectDir);
|
|
103
|
-
const settled = await
|
|
105
|
+
const settled = await mapBounded(ids, async (resourceId) => {
|
|
104
106
|
const metadata = await backend.resolveMetadata(config, canonical, resourceId, {
|
|
105
107
|
...options,
|
|
106
108
|
...session,
|
|
109
|
+
projectDir,
|
|
107
110
|
clientInitialized: true,
|
|
108
111
|
});
|
|
109
112
|
assertMetadataIdentity(metadata, canonical, resourceId);
|
|
@@ -158,7 +161,7 @@ async function checkoutResources(projectDir, resourceType, resourceIds, options
|
|
|
158
161
|
updated_by: metadata.updated_by,
|
|
159
162
|
};
|
|
160
163
|
return entry;
|
|
161
|
-
})
|
|
164
|
+
}, { ...options, settled: true });
|
|
162
165
|
const results = settled.filter((item) => item.status === 'fulfilled').map((item) => item.value);
|
|
163
166
|
for (const entry of results) manifest.entries[entryKey(canonical, entry.resource_id)] = entry;
|
|
164
167
|
if (results.length) await saveManifest(projectDir, manifest);
|
|
@@ -211,6 +214,7 @@ async function writeConflict(projectDir, entry, error, config, backend, options,
|
|
|
211
214
|
const remoteMetadata = await backend.resolveMetadata(config, entry.resource_type, entry.resource_id, {
|
|
212
215
|
...options,
|
|
213
216
|
...session,
|
|
217
|
+
projectDir,
|
|
214
218
|
clientInitialized: true,
|
|
215
219
|
});
|
|
216
220
|
assertMetadataIdentity(remoteMetadata, entry.resource_type, entry.resource_id);
|
|
@@ -270,9 +274,7 @@ function unresolvedConflict(projectDir, resourceType, resourceId) {
|
|
|
270
274
|
}
|
|
271
275
|
}
|
|
272
276
|
|
|
273
|
-
async function
|
|
274
|
-
const canonical = canonicalResourceType(resourceType);
|
|
275
|
-
const ids = ensureIds(resourceIds);
|
|
277
|
+
async function commitBatchUnlocked(projectDir, targets, options = {}) {
|
|
276
278
|
const config = options.config || loadProjectConfig(projectDir);
|
|
277
279
|
const backend = backendFor(options);
|
|
278
280
|
const session = await createSession(config, options);
|
|
@@ -287,8 +289,8 @@ async function commitResources(projectDir, resourceType, resourceIds, options =
|
|
|
287
289
|
};
|
|
288
290
|
|
|
289
291
|
// Resolve and validate every target before the first remote write. Different
|
|
290
|
-
// resources are independent
|
|
291
|
-
const preflight = await
|
|
292
|
+
// resources are independent; bound concurrency to avoid exhausting connections.
|
|
293
|
+
const preflight = await mapBounded(targets, async ({ resource_type: canonical, resource_id: resourceId }) => {
|
|
292
294
|
try {
|
|
293
295
|
const entry = getEntry(manifest, canonical, resourceId);
|
|
294
296
|
if (!entry) {
|
|
@@ -320,6 +322,7 @@ async function commitResources(projectDir, resourceType, resourceIds, options =
|
|
|
320
322
|
const fresh = await backend.resolveMetadata(config, canonical, resourceId, {
|
|
321
323
|
...options,
|
|
322
324
|
...session,
|
|
325
|
+
projectDir,
|
|
323
326
|
clientInitialized: true,
|
|
324
327
|
});
|
|
325
328
|
assertMetadataIdentity(fresh, canonical, resourceId);
|
|
@@ -359,22 +362,21 @@ async function commitResources(projectDir, resourceType, resourceIds, options =
|
|
|
359
362
|
details: error.details || {},
|
|
360
363
|
} };
|
|
361
364
|
}
|
|
362
|
-
})
|
|
365
|
+
}, options);
|
|
363
366
|
for (const item of preflight) {
|
|
364
367
|
if (item.plan) plans.push(item.plan);
|
|
365
368
|
else preflightFailures.push(item.failure);
|
|
366
369
|
}
|
|
367
370
|
|
|
368
371
|
if (preflightFailures.length) {
|
|
369
|
-
const failedIds = new Set(preflightFailures.map((item) =>
|
|
370
|
-
const notStarted =
|
|
371
|
-
|
|
372
|
-
resource_id: resourceId,
|
|
372
|
+
const failedIds = new Set(preflightFailures.map((item) => entryKey(item.resource_type, item.resource_id)));
|
|
373
|
+
const notStarted = targets.filter((item) => !failedIds.has(entryKey(item.resource_type, item.resource_id))).map((item) => ({
|
|
374
|
+
...item,
|
|
373
375
|
status: 'not_started',
|
|
374
376
|
phase: 'preflight',
|
|
375
377
|
}));
|
|
376
378
|
for (const result of [...preflightFailures, ...notStarted]) report(result);
|
|
377
|
-
if (
|
|
379
|
+
if (targets.length === 1 && preflightFailures.length === 1) {
|
|
378
380
|
const original = new WorktreeError(
|
|
379
381
|
preflightFailures[0].code,
|
|
380
382
|
preflightFailures[0].message,
|
|
@@ -391,6 +393,7 @@ async function commitResources(projectDir, resourceType, resourceIds, options =
|
|
|
391
393
|
|
|
392
394
|
async function commitPlan(plan) {
|
|
393
395
|
const { resourceId, entry, localPath, current, fresh } = plan;
|
|
396
|
+
const canonical = entry.resource_type;
|
|
394
397
|
if (current.hash === entry.base_hash) {
|
|
395
398
|
return { resource_type: canonical, resource_id: resourceId, status: 'unchanged', hash: current.hash };
|
|
396
399
|
}
|
|
@@ -430,6 +433,7 @@ async function commitResources(projectDir, resourceType, resourceIds, options =
|
|
|
430
433
|
const confirmed = await backend.resolveMetadata(config, canonical, resourceId, {
|
|
431
434
|
...options,
|
|
432
435
|
...session,
|
|
436
|
+
projectDir,
|
|
433
437
|
clientInitialized: true,
|
|
434
438
|
});
|
|
435
439
|
assertMetadataIdentity(confirmed, canonical, resourceId);
|
|
@@ -476,7 +480,7 @@ async function commitResources(projectDir, resourceType, resourceIds, options =
|
|
|
476
480
|
}
|
|
477
481
|
}
|
|
478
482
|
|
|
479
|
-
const settled = await
|
|
483
|
+
const settled = await mapBounded(plans, async (plan) => {
|
|
480
484
|
try {
|
|
481
485
|
const result = await commitPlan(plan);
|
|
482
486
|
report(result);
|
|
@@ -485,16 +489,16 @@ async function commitResources(projectDir, resourceType, resourceIds, options =
|
|
|
485
489
|
report(error.batchResult);
|
|
486
490
|
throw error;
|
|
487
491
|
}
|
|
488
|
-
})
|
|
492
|
+
}, { ...options, settled: true });
|
|
489
493
|
const committed = results.filter((item) => item.status === 'committed');
|
|
490
494
|
if (committed.length) await saveManifest(projectDir, manifest);
|
|
491
495
|
const failures = settled.filter((item) => item.status === 'rejected');
|
|
492
496
|
if (failures.length) {
|
|
493
497
|
const first = failures[0].reason;
|
|
494
|
-
const inputOrder = new Map(
|
|
498
|
+
const inputOrder = new Map(targets.map((item, index) => [entryKey(item.resource_type, item.resource_id), index]));
|
|
495
499
|
const ordered = (items) => [...items].sort((left, right) => (
|
|
496
|
-
(inputOrder.get(
|
|
497
|
-
- (inputOrder.get(
|
|
500
|
+
(inputOrder.get(entryKey(left.resource_type, left.resource_id)) ?? Number.MAX_SAFE_INTEGER)
|
|
501
|
+
- (inputOrder.get(entryKey(right.resource_type, right.resource_id)) ?? Number.MAX_SAFE_INTEGER)
|
|
498
502
|
));
|
|
499
503
|
first.details = {
|
|
500
504
|
...(first.details || {}),
|
|
@@ -509,7 +513,7 @@ async function commitResources(projectDir, resourceType, resourceIds, options =
|
|
|
509
513
|
return results;
|
|
510
514
|
}
|
|
511
515
|
|
|
512
|
-
async function
|
|
516
|
+
async function reconcileResourcesUnlocked(projectDir, resourceType, resourceIds, options = {}) {
|
|
513
517
|
const canonical = canonicalResourceType(resourceType);
|
|
514
518
|
const ids = ensureIds(resourceIds);
|
|
515
519
|
const config = options.config || loadProjectConfig(projectDir);
|
|
@@ -533,7 +537,7 @@ async function reconcileResources(projectDir, resourceType, resourceIds, options
|
|
|
533
537
|
);
|
|
534
538
|
}
|
|
535
539
|
const remote = await backend.resolveMetadata(config, canonical, resourceId, {
|
|
536
|
-
...options, ...session, clientInitialized: true,
|
|
540
|
+
...options, ...session, projectDir, clientInitialized: true,
|
|
537
541
|
});
|
|
538
542
|
assertMetadataIdentity(remote, canonical, resourceId);
|
|
539
543
|
const status = await inspectEntry(projectDir, entry, remote);
|
|
@@ -654,7 +658,7 @@ function showConflict(projectDir, resourceType, resourceId) {
|
|
|
654
658
|
return match;
|
|
655
659
|
}
|
|
656
660
|
|
|
657
|
-
async function
|
|
661
|
+
async function resolveConflictUnlocked(projectDir, resourceType, resourceId) {
|
|
658
662
|
const canonical = canonicalResourceType(resourceType);
|
|
659
663
|
const manifest = loadManifest(projectDir);
|
|
660
664
|
const entry = getEntry(manifest, canonical, resourceId);
|
|
@@ -695,7 +699,45 @@ async function resolveConflict(projectDir, resourceType, resourceId) {
|
|
|
695
699
|
return { ...resolved, manifest_path: relativePath(projectDir, manifestFile) };
|
|
696
700
|
}
|
|
697
701
|
|
|
702
|
+
function normalizedTargets(targets) {
|
|
703
|
+
const unique = new Map();
|
|
704
|
+
for (const target of targets) {
|
|
705
|
+
const resource_type = canonicalResourceType(target.resource_type);
|
|
706
|
+
const resource_id = ensureIds([target.resource_id])[0];
|
|
707
|
+
unique.set(entryKey(resource_type, resource_id), { resource_type, resource_id });
|
|
708
|
+
}
|
|
709
|
+
if (!unique.size) throw new WorktreeError('INVALID_RESOURCE_ID', 'At least one resource is required.');
|
|
710
|
+
return [...unique.values()];
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
function commitBatch(projectDir, targets, options = {}) {
|
|
714
|
+
const normalized = normalizedTargets(targets);
|
|
715
|
+
return withResourceLocks(projectDir, normalized.map((item) => entryKey(item.resource_type, item.resource_id)),
|
|
716
|
+
() => commitBatchUnlocked(projectDir, normalized, options));
|
|
717
|
+
}
|
|
718
|
+
function commitResources(projectDir, resourceType, resourceIds, options = {}) {
|
|
719
|
+
return commitBatch(projectDir, ensureIds(resourceIds).map((resource_id) => ({ resource_type: resourceType, resource_id })), options);
|
|
720
|
+
}
|
|
721
|
+
function checkoutResources(projectDir, resourceType, resourceIds, options = {}) {
|
|
722
|
+
const type = canonicalResourceType(resourceType);
|
|
723
|
+
const ids = ensureIds(resourceIds);
|
|
724
|
+
return withResourceLocks(projectDir, ids.map((id) => entryKey(type, id)),
|
|
725
|
+
() => checkoutResourcesUnlocked(projectDir, type, ids, options));
|
|
726
|
+
}
|
|
727
|
+
function reconcileResources(projectDir, resourceType, resourceIds, options = {}) {
|
|
728
|
+
const type = canonicalResourceType(resourceType);
|
|
729
|
+
const ids = ensureIds(resourceIds);
|
|
730
|
+
return withResourceLocks(projectDir, ids.map((id) => entryKey(type, id)),
|
|
731
|
+
() => reconcileResourcesUnlocked(projectDir, type, ids, options));
|
|
732
|
+
}
|
|
733
|
+
function resolveConflict(projectDir, resourceType, resourceId) {
|
|
734
|
+
const type = canonicalResourceType(resourceType);
|
|
735
|
+
return withResourceLocks(projectDir, [entryKey(type, resourceId)],
|
|
736
|
+
() => resolveConflictUnlocked(projectDir, type, resourceId));
|
|
737
|
+
}
|
|
738
|
+
|
|
698
739
|
module.exports = {
|
|
740
|
+
commitBatch,
|
|
699
741
|
CONFLICT_SCHEMA_VERSION,
|
|
700
742
|
worktreePaths,
|
|
701
743
|
conflictPaths,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
const { acquireLock, releaseLock } = require('../worklog');
|
|
7
|
+
const { WorktreeError } = require('./errors');
|
|
8
|
+
|
|
9
|
+
// Reuse owner-token locks, but never expire an active long-running transfer.
|
|
10
|
+
function lockFile(file) {
|
|
11
|
+
const lockPath = `${file}.lock`;
|
|
12
|
+
try {
|
|
13
|
+
const token = fs.readFileSync(lockPath, 'utf8').trim();
|
|
14
|
+
const pid = Number(token.split(':')[0]);
|
|
15
|
+
if (Number.isSafeInteger(pid) && pid > 0) {
|
|
16
|
+
try { process.kill(pid, 0); } catch (error) {
|
|
17
|
+
if (error.code === 'ESRCH' && fs.readFileSync(lockPath, 'utf8').trim() === token) fs.rmSync(lockPath);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
} catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
21
|
+
try { return acquireLock(file, { lockTimeoutMs: 0, lockStaleMs: Number.MAX_SAFE_INTEGER }); }
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error.code !== 'WORKLOG_LOCK_TIMEOUT') throw error;
|
|
24
|
+
throw new WorktreeError('WORKTREE_LOCKED', 'Another process owns this worktree resource; retry after it finishes.', { lock_path: lockPath });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function withResourceLocks(projectDir, keys, action) {
|
|
29
|
+
const locks = [];
|
|
30
|
+
try {
|
|
31
|
+
for (const key of [...new Set(keys)].sort()) {
|
|
32
|
+
const name = crypto.createHash('sha256').update(key).digest('hex');
|
|
33
|
+
locks.push(lockFile(path.join(projectDir, '.draftgo', 'worktree', '.locks', name)));
|
|
34
|
+
}
|
|
35
|
+
return await action();
|
|
36
|
+
} finally { for (const lock of locks.reverse()) releaseLock(lock); }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function withManifestLock(projectDir, action) {
|
|
40
|
+
const file = path.join(projectDir, '.draftgo', 'worktree', 'manifest.json');
|
|
41
|
+
const started = Date.now();
|
|
42
|
+
let lock;
|
|
43
|
+
while (!lock) {
|
|
44
|
+
try { lock = lockFile(file); } catch (error) {
|
|
45
|
+
if (error.code !== 'WORKTREE_LOCKED' || Date.now() - started >= 10000) throw error;
|
|
46
|
+
await new Promise((resolve) => setTimeout(resolve, 15));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
try { return await action(); } finally { releaseLock(lock); }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = { withResourceLocks, withManifestLock };
|
package/src/worktree/manifest.js
CHANGED
|
@@ -6,6 +6,8 @@ const { WorktreeError } = require('./errors');
|
|
|
6
6
|
const { entryKey } = require('./types');
|
|
7
7
|
const { writeJsonAtomic } = require('./streams');
|
|
8
8
|
|
|
9
|
+
const { withManifestLock } = require('./locks');
|
|
10
|
+
const snapshots = new WeakMap();
|
|
9
11
|
const MANIFEST_SCHEMA_VERSION = 1;
|
|
10
12
|
|
|
11
13
|
function manifestPath(projectDir) {
|
|
@@ -13,7 +15,9 @@ function manifestPath(projectDir) {
|
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
function emptyManifest() {
|
|
16
|
-
|
|
18
|
+
const value = { schema_version: MANIFEST_SCHEMA_VERSION, entries: {} };
|
|
19
|
+
snapshots.set(value, {});
|
|
20
|
+
return value;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
function loadManifest(projectDir) {
|
|
@@ -29,6 +33,7 @@ function loadManifest(projectDir) {
|
|
|
29
33
|
|| !parsed.entries || typeof parsed.entries !== 'object' || Array.isArray(parsed.entries)) {
|
|
30
34
|
throw new WorktreeError('INVALID_WORKTREE_MANIFEST', 'Unsupported or malformed checkout manifest.');
|
|
31
35
|
}
|
|
36
|
+
snapshots.set(parsed, JSON.parse(JSON.stringify(parsed.entries)));
|
|
32
37
|
return parsed;
|
|
33
38
|
}
|
|
34
39
|
|
|
@@ -58,9 +63,18 @@ function absolutePath(projectDir, relative) {
|
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
async function saveManifest(projectDir, manifest) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
const before = snapshots.get(manifest) || {};
|
|
67
|
+
await withManifestLock(projectDir, async () => {
|
|
68
|
+
const fresh = loadManifest(projectDir);
|
|
69
|
+
for (const key of new Set([...Object.keys(before), ...Object.keys(manifest.entries)])) {
|
|
70
|
+
if (JSON.stringify(before[key]) === JSON.stringify(manifest.entries[key])) continue;
|
|
71
|
+
if (manifest.entries[key] === undefined) delete fresh.entries[key];
|
|
72
|
+
else fresh.entries[key] = manifest.entries[key];
|
|
73
|
+
}
|
|
74
|
+
fresh.updated_at = new Date().toISOString();
|
|
75
|
+
await writeJsonAtomic(manifestPath(projectDir), fresh);
|
|
76
|
+
snapshots.set(manifest, JSON.parse(JSON.stringify(manifest.entries)));
|
|
77
|
+
});
|
|
64
78
|
}
|
|
65
79
|
|
|
66
80
|
module.exports = {
|
package/src/worktree/status.js
CHANGED
|
@@ -102,14 +102,16 @@ async function inspectRemoteCheckouts(projectDir, options = {}) {
|
|
|
102
102
|
? { client: options.client || {}, tools: options.tools || [] }
|
|
103
103
|
: await openMetadataSession(config, options);
|
|
104
104
|
const entries = options.entries || Object.values(manifest.entries);
|
|
105
|
-
|
|
105
|
+
const { mapBounded } = require('../mcp/parallel');
|
|
106
|
+
return mapBounded(entries, async (entry) => {
|
|
106
107
|
const remote = await backend.resolveMetadata(config, entry.resource_type, entry.resource_id, {
|
|
107
108
|
...options,
|
|
108
109
|
...session,
|
|
110
|
+
projectDir,
|
|
109
111
|
clientInitialized: true,
|
|
110
112
|
});
|
|
111
113
|
return inspectEntry(projectDir, entry, remote);
|
|
112
|
-
})
|
|
114
|
+
}, options);
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
module.exports = {
|