@claudetools/cli 0.13.12 → 0.13.15
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/__tests__/factories.d.ts +173 -0
- package/dist/__tests__/factories.d.ts.map +1 -0
- package/dist/__tests__/factories.js +150 -0
- package/dist/__tests__/factories.js.map +1 -0
- package/dist/__tests__/helpers.d.ts +36 -0
- package/dist/__tests__/helpers.d.ts.map +1 -0
- package/dist/__tests__/helpers.js +52 -0
- package/dist/__tests__/helpers.js.map +1 -0
- package/dist/analytics/index.d.ts +14 -0
- package/dist/analytics/index.d.ts.map +1 -0
- package/dist/analytics/index.js +259 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/session.d.ts +17 -0
- package/dist/analytics/session.d.ts.map +1 -0
- package/dist/analytics/session.js +130 -0
- package/dist/analytics/session.js.map +1 -0
- package/dist/analytics/token-tracker.d.ts +48 -0
- package/dist/analytics/token-tracker.d.ts.map +1 -0
- package/dist/analytics/token-tracker.js +269 -0
- package/dist/analytics/token-tracker.js.map +1 -0
- package/dist/analytics/tracker.d.ts +33 -0
- package/dist/analytics/tracker.d.ts.map +1 -0
- package/dist/analytics/tracker.js +210 -0
- package/dist/analytics/tracker.js.map +1 -0
- package/dist/api-keys/index.d.ts +15 -0
- package/dist/api-keys/index.d.ts.map +1 -0
- package/dist/api-keys/index.js +228 -0
- package/dist/api-keys/index.js.map +1 -0
- package/dist/auth/config.d.ts +15 -0
- package/dist/auth/config.d.ts.map +1 -0
- package/dist/auth/config.js +67 -0
- package/dist/auth/config.js.map +1 -0
- package/dist/auth/index.d.ts +8 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +299 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/keychain.d.ts +21 -0
- package/dist/auth/keychain.d.ts.map +1 -0
- package/dist/auth/keychain.js +256 -0
- package/dist/auth/keychain.js.map +1 -0
- package/dist/billing/index.d.ts +7 -0
- package/dist/billing/index.d.ts.map +1 -0
- package/dist/billing/index.js +233 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/cli.js +291 -43
- package/dist/cli.js.map +1 -1
- package/dist/commands/hook.d.ts +12 -0
- package/dist/commands/hook.d.ts.map +1 -0
- package/dist/commands/hook.js +190 -0
- package/dist/commands/hook.js.map +1 -0
- package/dist/commands/keys.d.ts +4 -0
- package/dist/commands/keys.d.ts.map +1 -0
- package/dist/commands/keys.js +43 -0
- package/dist/commands/keys.js.map +1 -0
- package/dist/commands/mcp.d.ts +4 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +43 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/project.d.ts +4 -0
- package/dist/commands/project.d.ts.map +1 -0
- package/dist/commands/project.js +68 -0
- package/dist/commands/project.js.map +1 -0
- package/dist/commands/skill.d.ts +4 -0
- package/dist/commands/skill.d.ts.map +1 -0
- package/dist/commands/skill.js +37 -0
- package/dist/commands/skill.js.map +1 -0
- package/dist/commands/stacks.d.ts +4 -0
- package/dist/commands/stacks.d.ts.map +1 -0
- package/dist/commands/stacks.js +103 -0
- package/dist/commands/stacks.js.map +1 -0
- package/dist/commands/stats.d.ts +4 -0
- package/dist/commands/stats.d.ts.map +1 -0
- package/dist/commands/stats.js +6 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/commands/sync.d.ts +4 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +60 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/update.d.ts +4 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +63 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/daemon/client.d.ts +107 -0
- package/dist/daemon/client.d.ts.map +1 -0
- package/dist/daemon/client.js +250 -0
- package/dist/daemon/client.js.map +1 -0
- package/dist/daemon/health.d.ts +38 -0
- package/dist/daemon/health.d.ts.map +1 -0
- package/dist/daemon/health.js +212 -0
- package/dist/daemon/health.js.map +1 -0
- package/dist/daemon/index.d.ts +34 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +197 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/protocol.d.ts +144 -0
- package/dist/daemon/protocol.d.ts.map +1 -0
- package/dist/daemon/protocol.js +5 -0
- package/dist/daemon/protocol.js.map +1 -0
- package/dist/gamification/index.d.ts +13 -0
- package/dist/gamification/index.d.ts.map +1 -0
- package/dist/gamification/index.js +120 -0
- package/dist/gamification/index.js.map +1 -0
- package/dist/gamification/types.d.ts +34 -0
- package/dist/gamification/types.d.ts.map +1 -0
- package/dist/gamification/types.js +5 -0
- package/dist/gamification/types.js.map +1 -0
- package/dist/hooks/index.d.ts +65 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +403 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/lib/api.d.ts +29 -0
- package/dist/lib/api.d.ts.map +1 -0
- package/dist/lib/api.js +213 -0
- package/dist/lib/api.js.map +1 -0
- package/dist/lib/browser.d.ts +6 -0
- package/dist/lib/browser.d.ts.map +1 -0
- package/dist/lib/browser.js +14 -0
- package/dist/lib/browser.js.map +1 -0
- package/dist/lib/channel-config.d.ts +10 -0
- package/dist/lib/channel-config.d.ts.map +1 -0
- package/dist/lib/channel-config.js +48 -0
- package/dist/lib/channel-config.js.map +1 -0
- package/dist/lib/command-runner.d.ts +16 -0
- package/dist/lib/command-runner.d.ts.map +1 -0
- package/dist/lib/command-runner.js +59 -0
- package/dist/lib/command-runner.js.map +1 -0
- package/dist/lib/command-utils.d.ts +22 -0
- package/dist/lib/command-utils.d.ts.map +1 -0
- package/dist/lib/command-utils.js +88 -0
- package/dist/lib/command-utils.js.map +1 -0
- package/dist/lib/error-handler.d.ts +13 -0
- package/dist/lib/error-handler.d.ts.map +1 -0
- package/dist/lib/error-handler.js +70 -0
- package/dist/lib/error-handler.js.map +1 -0
- package/dist/lib/errors.d.ts +35 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +70 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/exit.d.ts +10 -0
- package/dist/lib/exit.d.ts.map +1 -0
- package/dist/lib/exit.js +21 -0
- package/dist/lib/exit.js.map +1 -0
- package/dist/lib/formatters.d.ts +65 -0
- package/dist/lib/formatters.d.ts.map +1 -0
- package/dist/lib/formatters.js +180 -0
- package/dist/lib/formatters.js.map +1 -0
- package/dist/lib/hybrid-data.d.ts +47 -0
- package/dist/lib/hybrid-data.d.ts.map +1 -0
- package/dist/lib/hybrid-data.js +326 -0
- package/dist/lib/hybrid-data.js.map +1 -0
- package/dist/lib/local-store.d.ts +113 -0
- package/dist/lib/local-store.d.ts.map +1 -0
- package/dist/lib/local-store.js +220 -0
- package/dist/lib/local-store.js.map +1 -0
- package/dist/lib/machine-id.d.ts +8 -0
- package/dist/lib/machine-id.d.ts.map +1 -0
- package/dist/lib/machine-id.js +39 -0
- package/dist/lib/machine-id.js.map +1 -0
- package/dist/lib/network.d.ts +15 -0
- package/dist/lib/network.d.ts.map +1 -0
- package/dist/lib/network.js +46 -0
- package/dist/lib/network.js.map +1 -0
- package/dist/lib/theme.d.ts +77 -0
- package/dist/lib/theme.d.ts.map +1 -0
- package/dist/lib/theme.js +137 -0
- package/dist/lib/theme.js.map +1 -0
- package/dist/lib/tool-availability.d.ts +13 -0
- package/dist/lib/tool-availability.d.ts.map +1 -0
- package/dist/lib/tool-availability.js +48 -0
- package/dist/lib/tool-availability.js.map +1 -0
- package/dist/lib/update-checker.d.ts +21 -0
- package/dist/lib/update-checker.d.ts.map +1 -0
- package/dist/lib/update-checker.js +110 -0
- package/dist/lib/update-checker.js.map +1 -0
- package/dist/lib/validation.d.ts +30 -0
- package/dist/lib/validation.d.ts.map +1 -0
- package/dist/lib/validation.js +82 -0
- package/dist/lib/validation.js.map +1 -0
- package/dist/lib/validators.d.ts +18 -0
- package/dist/lib/validators.d.ts.map +1 -0
- package/dist/lib/validators.js +30 -0
- package/dist/lib/validators.js.map +1 -0
- package/dist/marketplace/api.d.ts +24 -0
- package/dist/marketplace/api.d.ts.map +1 -0
- package/dist/marketplace/api.js +92 -0
- package/dist/marketplace/api.js.map +1 -0
- package/dist/marketplace/index.d.ts +13 -0
- package/dist/marketplace/index.d.ts.map +1 -0
- package/dist/marketplace/index.js +155 -0
- package/dist/marketplace/index.js.map +1 -0
- package/dist/marketplace/installer.d.ts +18 -0
- package/dist/marketplace/installer.d.ts.map +1 -0
- package/dist/marketplace/installer.js +184 -0
- package/dist/marketplace/installer.js.map +1 -0
- package/dist/mcp/api.d.ts +93 -0
- package/dist/mcp/api.d.ts.map +1 -0
- package/dist/mcp/api.js +106 -0
- package/dist/mcp/api.js.map +1 -0
- package/dist/mcp/config.d.ts +72 -0
- package/dist/mcp/config.d.ts.map +1 -0
- package/dist/mcp/config.js +156 -0
- package/dist/mcp/config.js.map +1 -0
- package/dist/mcp/index.d.ts +54 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +381 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/prompt.clean.d.ts +25 -0
- package/dist/mcp/prompt.clean.d.ts.map +1 -0
- package/dist/mcp/prompt.clean.js +206 -0
- package/dist/mcp/prompt.clean.js.map +1 -0
- package/dist/mcp/prompt.d.ts +52 -0
- package/dist/mcp/prompt.d.ts.map +1 -0
- package/dist/mcp/prompt.js +210 -0
- package/dist/mcp/prompt.js.map +1 -0
- package/dist/mcp/secrets.clean.d.ts +18 -0
- package/dist/mcp/secrets.clean.d.ts.map +1 -0
- package/dist/mcp/secrets.clean.js +357 -0
- package/dist/mcp/secrets.clean.js.map +1 -0
- package/dist/mcp/secrets.d.ts +46 -0
- package/dist/mcp/secrets.d.ts.map +1 -0
- package/dist/mcp/secrets.js +339 -0
- package/dist/mcp/secrets.js.map +1 -0
- package/dist/memory/index.d.ts +14 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +98 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/onboard/agents-md-builder.d.ts.map +1 -1
- package/dist/onboard/agents-md-builder.js +45 -0
- package/dist/onboard/agents-md-builder.js.map +1 -1
- package/dist/onboard/claude-inference.d.ts.map +1 -1
- package/dist/onboard/claude-inference.js +31 -5
- package/dist/onboard/claude-inference.js.map +1 -1
- package/dist/onboard/context7-fetcher.d.ts +1 -1
- package/dist/onboard/context7-fetcher.d.ts.map +1 -1
- package/dist/onboard/context7-fetcher.js +50 -16
- package/dist/onboard/context7-fetcher.js.map +1 -1
- package/dist/onboard/docs-builder.d.ts.map +1 -1
- package/dist/onboard/docs-builder.js +523 -50
- package/dist/onboard/docs-builder.js.map +1 -1
- package/dist/onboard/index.d.ts.map +1 -1
- package/dist/onboard/index.js +74 -25
- package/dist/onboard/index.js.map +1 -1
- package/dist/onboard/stack-detector.d.ts.map +1 -1
- package/dist/onboard/stack-detector.js +5 -55
- package/dist/onboard/stack-detector.js.map +1 -1
- package/dist/project/constants.d.ts +21 -0
- package/dist/project/constants.d.ts.map +1 -0
- package/dist/project/constants.js +21 -0
- package/dist/project/constants.js.map +1 -0
- package/dist/project/format.d.ts +16 -0
- package/dist/project/format.d.ts.map +1 -0
- package/dist/project/format.js +40 -0
- package/dist/project/format.js.map +1 -0
- package/dist/project/git.d.ts +28 -0
- package/dist/project/git.d.ts.map +1 -0
- package/dist/project/git.js +93 -0
- package/dist/project/git.js.map +1 -0
- package/dist/project/index.d.ts +36 -0
- package/dist/project/index.d.ts.map +1 -0
- package/dist/project/index.js +272 -0
- package/dist/project/index.js.map +1 -0
- package/dist/project/mapper.d.ts +27 -0
- package/dist/project/mapper.d.ts.map +1 -0
- package/dist/project/mapper.js +64 -0
- package/dist/project/mapper.js.map +1 -0
- package/dist/project/storage.d.ts +71 -0
- package/dist/project/storage.d.ts.map +1 -0
- package/dist/project/storage.js +274 -0
- package/dist/project/storage.js.map +1 -0
- package/dist/project/sync-bridge.d.ts +33 -0
- package/dist/project/sync-bridge.d.ts.map +1 -0
- package/dist/project/sync-bridge.js +155 -0
- package/dist/project/sync-bridge.js.map +1 -0
- package/dist/project/types.d.ts +107 -0
- package/dist/project/types.d.ts.map +1 -0
- package/dist/project/types.js +77 -0
- package/dist/project/types.js.map +1 -0
- package/dist/publish/index.d.ts +4 -0
- package/dist/publish/index.d.ts.map +1 -0
- package/dist/publish/index.js +92 -0
- package/dist/publish/index.js.map +1 -0
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +29 -10
- package/dist/setup.js.map +1 -1
- package/dist/skills/index.d.ts +51 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +509 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/stacks/check.d.ts +2 -0
- package/dist/stacks/check.d.ts.map +1 -0
- package/dist/stacks/check.js +144 -0
- package/dist/stacks/check.js.map +1 -0
- package/dist/stacks/diff.d.ts +11 -0
- package/dist/stacks/diff.d.ts.map +1 -0
- package/dist/stacks/diff.js +123 -0
- package/dist/stacks/diff.js.map +1 -0
- package/dist/stacks/index.d.ts +17 -0
- package/dist/stacks/index.d.ts.map +1 -0
- package/dist/stacks/index.js +525 -0
- package/dist/stacks/index.js.map +1 -0
- package/dist/stacks/index.refactored.d.ts.map +1 -0
- package/dist/stacks/index.refactored.js.map +1 -0
- package/dist/stacks/io.d.ts +11 -0
- package/dist/stacks/io.d.ts.map +1 -0
- package/dist/stacks/io.js +179 -0
- package/dist/stacks/io.js.map +1 -0
- package/dist/stacks/rollback.d.ts +5 -0
- package/dist/stacks/rollback.d.ts.map +1 -0
- package/dist/stacks/rollback.js +162 -0
- package/dist/stacks/rollback.js.map +1 -0
- package/dist/stacks/types.d.ts +70 -0
- package/dist/stacks/types.d.ts.map +1 -0
- package/dist/stacks/types.js +6 -0
- package/dist/stacks/types.js.map +1 -0
- package/dist/stacks/utils.d.ts +9 -0
- package/dist/stacks/utils.d.ts.map +1 -0
- package/dist/stacks/utils.js +11 -0
- package/dist/stacks/utils.js.map +1 -0
- package/dist/start/index.d.ts +23 -0
- package/dist/start/index.d.ts.map +1 -0
- package/dist/start/index.js +386 -0
- package/dist/start/index.js.map +1 -0
- package/dist/sync/index.d.ts +49 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +207 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync-engine/__tests__/test-helpers.d.ts +14 -0
- package/dist/sync-engine/__tests__/test-helpers.d.ts.map +1 -0
- package/dist/sync-engine/__tests__/test-helpers.js +73 -0
- package/dist/sync-engine/__tests__/test-helpers.js.map +1 -0
- package/dist/sync-engine/client.d.ts +128 -0
- package/dist/sync-engine/client.d.ts.map +1 -0
- package/dist/sync-engine/client.js +289 -0
- package/dist/sync-engine/client.js.map +1 -0
- package/dist/sync-engine/health.d.ts +38 -0
- package/dist/sync-engine/health.d.ts.map +1 -0
- package/dist/sync-engine/health.js +259 -0
- package/dist/sync-engine/health.js.map +1 -0
- package/dist/sync-engine/index.d.ts +34 -0
- package/dist/sync-engine/index.d.ts.map +1 -0
- package/dist/sync-engine/index.js +197 -0
- package/dist/sync-engine/index.js.map +1 -0
- package/dist/sync-engine/protocol.d.ts +153 -0
- package/dist/sync-engine/protocol.d.ts.map +1 -0
- package/dist/sync-engine/protocol.js +5 -0
- package/dist/sync-engine/protocol.js.map +1 -0
- package/dist/tasks/index.d.ts +14 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/index.js +109 -0
- package/dist/tasks/index.js.map +1 -0
- package/dist/team/index.d.ts +12 -0
- package/dist/team/index.d.ts.map +1 -0
- package/dist/team/index.js +151 -0
- package/dist/team/index.js.map +1 -0
- package/dist/updater.d.ts +5 -5
- package/dist/updater.d.ts.map +1 -1
- package/dist/updater.js +24 -88
- package/dist/updater.js.map +1 -1
- package/dist/usage/index.d.ts +10 -0
- package/dist/usage/index.d.ts.map +1 -0
- package/dist/usage/index.js +104 -0
- package/dist/usage/index.js.map +1 -0
- package/dist/webhooks/index.d.ts +7 -0
- package/dist/webhooks/index.d.ts.map +1 -0
- package/dist/webhooks/index.js +81 -0
- package/dist/webhooks/index.js.map +1 -0
- package/package.json +26 -15
- package/scripts/postinstall.js +282 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Storage
|
|
3
|
+
* Local file operations for ~/.claudetools/projects.json
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, openSync, closeSync, unlinkSync, } from 'node:fs';
|
|
6
|
+
import { homedir } from 'node:os';
|
|
7
|
+
import { join, dirname } from 'node:path';
|
|
8
|
+
import { PROJECTS_SCHEMA_VERSION, computePathHash, } from './types.js';
|
|
9
|
+
import { LOCK_MAX_RETRIES, LOCK_RETRY_DELAY } from './constants.js';
|
|
10
|
+
/**
|
|
11
|
+
* Get the path to the projects file
|
|
12
|
+
*/
|
|
13
|
+
export function getProjectsPath() {
|
|
14
|
+
return join(homedir(), '.claudetools', 'projects.json');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Migrate projects file to current schema version
|
|
18
|
+
*/
|
|
19
|
+
function migrateProjects(data) {
|
|
20
|
+
const file = data;
|
|
21
|
+
let version = file.version ?? 0;
|
|
22
|
+
let projects = (file.projects ?? []);
|
|
23
|
+
// v0 -> v1: Add sync status fields
|
|
24
|
+
if (version < 1) {
|
|
25
|
+
projects = projects.map((p) => ({
|
|
26
|
+
...p,
|
|
27
|
+
isSynced: p.isSynced ?? false,
|
|
28
|
+
isDeleted: p.isDeleted ?? false,
|
|
29
|
+
updatedAt: p.updatedAt ?? p.registeredAt,
|
|
30
|
+
}));
|
|
31
|
+
version = 1;
|
|
32
|
+
}
|
|
33
|
+
return { version, projects };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Load projects from disk
|
|
37
|
+
* Returns default file if not exists or parse fails
|
|
38
|
+
*/
|
|
39
|
+
export function loadProjects() {
|
|
40
|
+
const path = getProjectsPath();
|
|
41
|
+
if (!existsSync(path)) {
|
|
42
|
+
return { version: PROJECTS_SCHEMA_VERSION, projects: [] };
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const content = readFileSync(path, 'utf-8');
|
|
46
|
+
const data = JSON.parse(content);
|
|
47
|
+
return migrateProjects(data);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// If parse fails, backup and start fresh
|
|
51
|
+
const backup = `${path}.backup.${Date.now()}`;
|
|
52
|
+
try {
|
|
53
|
+
const content = readFileSync(path, 'utf-8');
|
|
54
|
+
writeFileSync(backup, content);
|
|
55
|
+
console.warn(`Projects file corrupted, backed up to: ${backup}`);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Ignore backup failures
|
|
59
|
+
}
|
|
60
|
+
return { version: PROJECTS_SCHEMA_VERSION, projects: [] };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Save projects to disk atomically
|
|
65
|
+
*/
|
|
66
|
+
export function saveProjects(file) {
|
|
67
|
+
const path = getProjectsPath();
|
|
68
|
+
const dir = dirname(path);
|
|
69
|
+
// Ensure directory exists
|
|
70
|
+
if (!existsSync(dir)) {
|
|
71
|
+
mkdirSync(dir, { recursive: true });
|
|
72
|
+
}
|
|
73
|
+
const content = JSON.stringify(file, null, 2);
|
|
74
|
+
writeFileSync(path, content, 'utf-8');
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Simple file-based lock using a .lock file
|
|
78
|
+
* Works cross-platform without native dependencies
|
|
79
|
+
*/
|
|
80
|
+
function getLockPath() {
|
|
81
|
+
return `${getProjectsPath()}.lock`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Try to acquire a file lock
|
|
85
|
+
* Returns a release function, or null if lock cannot be acquired
|
|
86
|
+
*/
|
|
87
|
+
async function tryAcquireLock(maxRetries = LOCK_MAX_RETRIES, retryDelay = LOCK_RETRY_DELAY) {
|
|
88
|
+
const lockPath = getLockPath();
|
|
89
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
90
|
+
try {
|
|
91
|
+
// O_EXCL ensures atomic creation - fails if file exists
|
|
92
|
+
const fd = openSync(lockPath, 'wx');
|
|
93
|
+
closeSync(fd);
|
|
94
|
+
// Return release function
|
|
95
|
+
return () => {
|
|
96
|
+
try {
|
|
97
|
+
unlinkSync(lockPath);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Ignore errors on unlock
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
const error = err;
|
|
106
|
+
if (error.code === 'EEXIST') {
|
|
107
|
+
// Lock file exists, wait and retry
|
|
108
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay * (attempt + 1)));
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
// Other error, rethrow
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Execute a function with file lock to prevent race conditions
|
|
119
|
+
* between CLI and desktop app
|
|
120
|
+
*/
|
|
121
|
+
export async function withProjectsLock(fn) {
|
|
122
|
+
const path = getProjectsPath();
|
|
123
|
+
const dir = dirname(path);
|
|
124
|
+
// Ensure directory and file exist for locking
|
|
125
|
+
if (!existsSync(dir)) {
|
|
126
|
+
mkdirSync(dir, { recursive: true });
|
|
127
|
+
}
|
|
128
|
+
if (!existsSync(path)) {
|
|
129
|
+
saveProjects({ version: PROJECTS_SCHEMA_VERSION, projects: [] });
|
|
130
|
+
}
|
|
131
|
+
const release = await tryAcquireLock();
|
|
132
|
+
if (!release) {
|
|
133
|
+
throw new Error('Could not acquire lock on projects file - another process may be using it');
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
return await fn();
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
release();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get all active (non-deleted) projects
|
|
144
|
+
*/
|
|
145
|
+
export function getActiveProjects() {
|
|
146
|
+
const file = loadProjects();
|
|
147
|
+
return file.projects.filter((p) => !p.isDeleted);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Find a project by ID
|
|
151
|
+
*/
|
|
152
|
+
export function findProjectById(id) {
|
|
153
|
+
const file = loadProjects();
|
|
154
|
+
return file.projects.find((p) => p.id === id && !p.isDeleted) ?? null;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Find a project by name (case-insensitive)
|
|
158
|
+
*/
|
|
159
|
+
export function findProjectByName(name) {
|
|
160
|
+
const file = loadProjects();
|
|
161
|
+
const lower = name.toLowerCase();
|
|
162
|
+
return (file.projects.find((p) => p.name.toLowerCase() === lower && !p.isDeleted) ??
|
|
163
|
+
null);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Find a project by path
|
|
167
|
+
*/
|
|
168
|
+
export function findProjectByPath(path) {
|
|
169
|
+
const pathHash = computePathHash(path);
|
|
170
|
+
const file = loadProjects();
|
|
171
|
+
return (file.projects.find((p) => p.pathHash === pathHash && !p.isDeleted) ?? null);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Find a project by name, ID, or path
|
|
175
|
+
*/
|
|
176
|
+
export function findProject(identifier) {
|
|
177
|
+
// Try by ID first
|
|
178
|
+
let project = findProjectById(identifier);
|
|
179
|
+
if (project)
|
|
180
|
+
return project;
|
|
181
|
+
// Try by name
|
|
182
|
+
project = findProjectByName(identifier);
|
|
183
|
+
if (project)
|
|
184
|
+
return project;
|
|
185
|
+
// Try by path
|
|
186
|
+
project = findProjectByPath(identifier);
|
|
187
|
+
return project;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Add a new project
|
|
191
|
+
*/
|
|
192
|
+
export async function addProject(project) {
|
|
193
|
+
await withProjectsLock(async () => {
|
|
194
|
+
const file = loadProjects();
|
|
195
|
+
// Check for duplicate path
|
|
196
|
+
const existing = file.projects.find((p) => p.pathHash === project.pathHash && !p.isDeleted);
|
|
197
|
+
if (existing) {
|
|
198
|
+
throw new Error(`Project already registered at this path: ${existing.name}`);
|
|
199
|
+
}
|
|
200
|
+
file.projects.push(project);
|
|
201
|
+
saveProjects(file);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Update an existing project
|
|
206
|
+
*/
|
|
207
|
+
export async function updateProject(id, updates) {
|
|
208
|
+
return withProjectsLock(async () => {
|
|
209
|
+
const file = loadProjects();
|
|
210
|
+
const index = file.projects.findIndex((p) => p.id === id);
|
|
211
|
+
if (index === -1) {
|
|
212
|
+
throw new Error(`Project not found: ${id}`);
|
|
213
|
+
}
|
|
214
|
+
const project = file.projects[index];
|
|
215
|
+
const updated = {
|
|
216
|
+
...project,
|
|
217
|
+
name: updates.name ?? project.name,
|
|
218
|
+
description: updates.description ?? project.description,
|
|
219
|
+
teamId: updates.teamId ?? project.teamId,
|
|
220
|
+
gitRemoteUrl: updates.gitRemoteUrl ?? project.gitRemoteUrl,
|
|
221
|
+
updatedAt: new Date().toISOString(),
|
|
222
|
+
isSynced: false, // Mark as needing sync after update
|
|
223
|
+
settings: updates.settings ?? project.settings,
|
|
224
|
+
stackProfile: updates.stackProfile ?? project.stackProfile,
|
|
225
|
+
};
|
|
226
|
+
file.projects[index] = updated;
|
|
227
|
+
saveProjects(file);
|
|
228
|
+
return updated;
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Soft delete a project (marks as deleted)
|
|
233
|
+
*/
|
|
234
|
+
export async function removeProject(id) {
|
|
235
|
+
await withProjectsLock(async () => {
|
|
236
|
+
const file = loadProjects();
|
|
237
|
+
const index = file.projects.findIndex((p) => p.id === id);
|
|
238
|
+
if (index === -1) {
|
|
239
|
+
throw new Error(`Project not found: ${id}`);
|
|
240
|
+
}
|
|
241
|
+
const project = file.projects[index];
|
|
242
|
+
const now = new Date().toISOString();
|
|
243
|
+
// Mark as deleted (soft delete)
|
|
244
|
+
file.projects[index] = {
|
|
245
|
+
...project,
|
|
246
|
+
updatedAt: now,
|
|
247
|
+
isSynced: false,
|
|
248
|
+
isDeleted: true,
|
|
249
|
+
deletedAt: now,
|
|
250
|
+
};
|
|
251
|
+
saveProjects(file);
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Permanently delete projects marked as deleted for over 30 days
|
|
256
|
+
*/
|
|
257
|
+
export async function cleanupDeletedProjects() {
|
|
258
|
+
return withProjectsLock(async () => {
|
|
259
|
+
const file = loadProjects();
|
|
260
|
+
const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1000;
|
|
261
|
+
const originalCount = file.projects.length;
|
|
262
|
+
file.projects = file.projects.filter((p) => {
|
|
263
|
+
if (!p.isDeleted || !p.deletedAt)
|
|
264
|
+
return true;
|
|
265
|
+
return new Date(p.deletedAt).getTime() > thirtyDaysAgo;
|
|
266
|
+
});
|
|
267
|
+
const removed = originalCount - file.projects.length;
|
|
268
|
+
if (removed > 0) {
|
|
269
|
+
saveProjects(file);
|
|
270
|
+
}
|
|
271
|
+
return removed;
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/project/storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Sync Bridge
|
|
3
|
+
* Fire-and-forget sync calls invoked by project commands after local writes.
|
|
4
|
+
* All operations are best-effort — local operations always succeed regardless of sync status.
|
|
5
|
+
*/
|
|
6
|
+
import type { Project } from './types.js';
|
|
7
|
+
import type { SyncProject } from '../sync-engine/protocol.js';
|
|
8
|
+
/**
|
|
9
|
+
* Check whether the sync engine is running and the user is logged in.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isSyncAvailable(): Promise<boolean>;
|
|
12
|
+
/**
|
|
13
|
+
* Push a project to the sync engine after a local write.
|
|
14
|
+
* On success, marks the project as synced in projects.json.
|
|
15
|
+
* On failure, logs a warning and leaves isSynced as false.
|
|
16
|
+
*/
|
|
17
|
+
export declare function pushProjectToEngine(project: Project, action: 'insert' | 'update'): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Delete a project from the sync engine.
|
|
20
|
+
*/
|
|
21
|
+
export declare function deleteProjectFromEngine(projectId: string): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Pull all projects from the sync engine.
|
|
24
|
+
*/
|
|
25
|
+
export declare function pullProjectsFromEngine(): Promise<SyncProject[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Merge cloud projects into local storage.
|
|
28
|
+
* - New cloud projects are inserted with default local-only fields.
|
|
29
|
+
* - Existing projects are updated if the cloud version is newer.
|
|
30
|
+
* - Local-only fields (pathHash, isSynced, settings, stackProfile) are preserved.
|
|
31
|
+
*/
|
|
32
|
+
export declare function mergeCloudProjects(cloudProjects: SyncProject[]): Promise<void>;
|
|
33
|
+
//# sourceMappingURL=sync-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-bridge.d.ts","sourceRoot":"","sources":["../../src/project/sync-bridge.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAM9D;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAMxD;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAC1B,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAa9E;AAED;;GAEG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAMrE;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,aAAa,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqEpF"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Sync Bridge
|
|
3
|
+
* Fire-and-forget sync calls invoked by project commands after local writes.
|
|
4
|
+
* All operations are best-effort — local operations always succeed regardless of sync status.
|
|
5
|
+
*/
|
|
6
|
+
import { defaultSettings, computePathHash } from './types.js';
|
|
7
|
+
import { getSyncEngineClient, isSyncEngineRunning } from '../sync-engine/client.js';
|
|
8
|
+
import { loadConfig } from '../auth/config.js';
|
|
9
|
+
import { loadProjects, saveProjects } from './storage.js';
|
|
10
|
+
import { projectToSyncProject, syncProjectToProject } from './mapper.js';
|
|
11
|
+
/**
|
|
12
|
+
* Check whether the sync engine is running and the user is logged in.
|
|
13
|
+
*/
|
|
14
|
+
export async function isSyncAvailable() {
|
|
15
|
+
const config = loadConfig();
|
|
16
|
+
if (!config.userId) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return isSyncEngineRunning();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Push a project to the sync engine after a local write.
|
|
23
|
+
* On success, marks the project as synced in projects.json.
|
|
24
|
+
* On failure, logs a warning and leaves isSynced as false.
|
|
25
|
+
*/
|
|
26
|
+
export async function pushProjectToEngine(project, action) {
|
|
27
|
+
const config = loadConfig();
|
|
28
|
+
if (!config.userId) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const engineRunning = await isSyncEngineRunning();
|
|
32
|
+
if (!engineRunning) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const syncProject = projectToSyncProject(project, config.userId);
|
|
36
|
+
try {
|
|
37
|
+
const client = getSyncEngineClient();
|
|
38
|
+
await client.syncPushProject(syncProject, action);
|
|
39
|
+
// Mark as synced in local storage
|
|
40
|
+
const file = loadProjects();
|
|
41
|
+
const projectIndex = file.projects.findIndex((p) => p.id === project.id);
|
|
42
|
+
if (projectIndex !== -1) {
|
|
43
|
+
const now = new Date().toISOString();
|
|
44
|
+
file.projects[projectIndex] = {
|
|
45
|
+
...file.projects[projectIndex],
|
|
46
|
+
isSynced: true,
|
|
47
|
+
lastSyncedAt: now,
|
|
48
|
+
};
|
|
49
|
+
saveProjects(file);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (syncError) {
|
|
53
|
+
const message = syncError instanceof Error ? syncError.message : String(syncError);
|
|
54
|
+
console.warn(` Warning: Failed to sync project to cloud: ${message}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Delete a project from the sync engine.
|
|
59
|
+
*/
|
|
60
|
+
export async function deleteProjectFromEngine(projectId) {
|
|
61
|
+
const engineRunning = await isSyncEngineRunning();
|
|
62
|
+
if (!engineRunning) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const client = getSyncEngineClient();
|
|
67
|
+
await client.syncDeleteProject(projectId);
|
|
68
|
+
}
|
|
69
|
+
catch (syncError) {
|
|
70
|
+
const message = syncError instanceof Error ? syncError.message : String(syncError);
|
|
71
|
+
console.warn(` Warning: Failed to delete project from cloud: ${message}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Pull all projects from the sync engine.
|
|
76
|
+
*/
|
|
77
|
+
export async function pullProjectsFromEngine() {
|
|
78
|
+
if (!(await isSyncAvailable())) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
const client = getSyncEngineClient();
|
|
82
|
+
return client.syncPullProjects();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Merge cloud projects into local storage.
|
|
86
|
+
* - New cloud projects are inserted with default local-only fields.
|
|
87
|
+
* - Existing projects are updated if the cloud version is newer.
|
|
88
|
+
* - Local-only fields (pathHash, isSynced, settings, stackProfile) are preserved.
|
|
89
|
+
*/
|
|
90
|
+
export async function mergeCloudProjects(cloudProjects) {
|
|
91
|
+
const file = loadProjects();
|
|
92
|
+
let modified = false;
|
|
93
|
+
for (const cloudProject of cloudProjects) {
|
|
94
|
+
const localIndex = file.projects.findIndex((p) => p.id === cloudProject.id);
|
|
95
|
+
if (localIndex === -1) {
|
|
96
|
+
// New project from cloud — insert with defaults
|
|
97
|
+
const partial = syncProjectToProject(cloudProject);
|
|
98
|
+
// Skip cloud-only projects without a local path — they'll appear
|
|
99
|
+
// in the web dashboard but not in CLI until explicitly bound
|
|
100
|
+
if (!partial.path) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const newProject = {
|
|
104
|
+
id: partial.id,
|
|
105
|
+
path: partial.path ?? '',
|
|
106
|
+
pathHash: partial.path ? computePathHash(partial.path) : '',
|
|
107
|
+
name: partial.name,
|
|
108
|
+
description: partial.description,
|
|
109
|
+
teamId: partial.teamId,
|
|
110
|
+
gitRemoteUrl: partial.gitRemoteUrl,
|
|
111
|
+
registeredAt: partial.registeredAt,
|
|
112
|
+
updatedAt: partial.updatedAt,
|
|
113
|
+
isSynced: true,
|
|
114
|
+
isDeleted: partial.isDeleted ?? false,
|
|
115
|
+
deletedAt: partial.deletedAt,
|
|
116
|
+
settings: defaultSettings(),
|
|
117
|
+
};
|
|
118
|
+
file.projects.push(newProject);
|
|
119
|
+
modified = true;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
// Existing project — update if cloud is newer
|
|
123
|
+
const localProject = file.projects[localIndex];
|
|
124
|
+
const cloudUpdatedAt = new Date(cloudProject.updated_at).getTime();
|
|
125
|
+
const localUpdatedAt = new Date(localProject.updatedAt).getTime();
|
|
126
|
+
if (cloudUpdatedAt > localUpdatedAt) {
|
|
127
|
+
const partial = syncProjectToProject(cloudProject);
|
|
128
|
+
file.projects[localIndex] = {
|
|
129
|
+
...localProject,
|
|
130
|
+
// Overwrite synced fields from cloud
|
|
131
|
+
name: partial.name ?? localProject.name,
|
|
132
|
+
description: partial.description,
|
|
133
|
+
teamId: partial.teamId,
|
|
134
|
+
gitRemoteUrl: partial.gitRemoteUrl,
|
|
135
|
+
path: partial.path ?? localProject.path,
|
|
136
|
+
pathHash: partial.path ? computePathHash(partial.path) : localProject.pathHash,
|
|
137
|
+
registeredAt: partial.registeredAt ?? localProject.registeredAt,
|
|
138
|
+
updatedAt: partial.updatedAt ?? localProject.updatedAt,
|
|
139
|
+
isDeleted: partial.isDeleted ?? localProject.isDeleted,
|
|
140
|
+
deletedAt: partial.deletedAt,
|
|
141
|
+
// Preserve local-only fields
|
|
142
|
+
isSynced: true,
|
|
143
|
+
lastSyncedAt: new Date().toISOString(),
|
|
144
|
+
settings: localProject.settings,
|
|
145
|
+
stackProfile: localProject.stackProfile,
|
|
146
|
+
};
|
|
147
|
+
modified = true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (modified) {
|
|
152
|
+
saveProjects(file);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=sync-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-bridge.js","sourceRoot":"","sources":["../../src/project/sync-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Registration Types
|
|
3
|
+
* Local-first project tracking for ClaudeTools
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Stack profile detected from session files
|
|
7
|
+
*/
|
|
8
|
+
export interface StackProfile {
|
|
9
|
+
primaryLanguage: string;
|
|
10
|
+
languages: Record<string, number>;
|
|
11
|
+
frameworks: string[];
|
|
12
|
+
detectedAt: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Project settings
|
|
16
|
+
*/
|
|
17
|
+
export interface ProjectSettings {
|
|
18
|
+
autoSync: boolean;
|
|
19
|
+
visibility: 'personal' | 'team';
|
|
20
|
+
budgetAlertUsd?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A registered project
|
|
24
|
+
*/
|
|
25
|
+
export interface Project {
|
|
26
|
+
id: string;
|
|
27
|
+
path: string;
|
|
28
|
+
pathHash: string;
|
|
29
|
+
name: string;
|
|
30
|
+
description?: string;
|
|
31
|
+
teamId?: string;
|
|
32
|
+
gitRemoteUrl?: string;
|
|
33
|
+
registeredAt: string;
|
|
34
|
+
updatedAt: string;
|
|
35
|
+
lastSyncedAt?: string;
|
|
36
|
+
isSynced: boolean;
|
|
37
|
+
isDeleted: boolean;
|
|
38
|
+
deletedAt?: string;
|
|
39
|
+
settings: ProjectSettings;
|
|
40
|
+
stackProfile?: StackProfile;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The projects file stored at ~/.claudetools/projects.json
|
|
44
|
+
*/
|
|
45
|
+
export interface ProjectsFile {
|
|
46
|
+
version: number;
|
|
47
|
+
projects: Project[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Unregistered project discovered from session files
|
|
51
|
+
*/
|
|
52
|
+
export interface UnregisteredProject {
|
|
53
|
+
path: string;
|
|
54
|
+
pathHash: string;
|
|
55
|
+
suggestedName: string;
|
|
56
|
+
detectedStack?: StackProfile;
|
|
57
|
+
sessionCount: number;
|
|
58
|
+
lastActivity?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Project statistics
|
|
62
|
+
*/
|
|
63
|
+
export interface ProjectStats {
|
|
64
|
+
sessionCount: number;
|
|
65
|
+
totalTokens: number;
|
|
66
|
+
inputTokens: number;
|
|
67
|
+
outputTokens: number;
|
|
68
|
+
cacheReadTokens: number;
|
|
69
|
+
costUsd: number;
|
|
70
|
+
lastActivity: string | null;
|
|
71
|
+
sparklineData: number[];
|
|
72
|
+
weekOverWeekChange: number;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Current schema version
|
|
76
|
+
*/
|
|
77
|
+
export declare const PROJECTS_SCHEMA_VERSION = 1;
|
|
78
|
+
/**
|
|
79
|
+
* Normalize a path for consistent hashing
|
|
80
|
+
* - Resolves to absolute path
|
|
81
|
+
* - Lowercases on case-insensitive systems
|
|
82
|
+
* - Removes trailing slashes
|
|
83
|
+
*/
|
|
84
|
+
export declare function normalizePath(inputPath: string): string;
|
|
85
|
+
/**
|
|
86
|
+
* Compute SHA256 hash of a normalized path
|
|
87
|
+
*/
|
|
88
|
+
export declare function computePathHash(inputPath: string): string;
|
|
89
|
+
/**
|
|
90
|
+
* Generate a new project ID
|
|
91
|
+
*/
|
|
92
|
+
export declare function generateProjectId(): string;
|
|
93
|
+
/**
|
|
94
|
+
* Create default project settings
|
|
95
|
+
*/
|
|
96
|
+
export declare function defaultSettings(): ProjectSettings;
|
|
97
|
+
/**
|
|
98
|
+
* Create a new project with defaults
|
|
99
|
+
*/
|
|
100
|
+
export declare function createProject(path: string, name: string, options?: {
|
|
101
|
+
description?: string;
|
|
102
|
+
teamId?: string;
|
|
103
|
+
gitRemoteUrl?: string;
|
|
104
|
+
settings?: Partial<ProjectSettings>;
|
|
105
|
+
stackProfile?: StackProfile;
|
|
106
|
+
}): Project;
|
|
107
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/project/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,UAAU,GAAG,MAAM,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,QAAQ,EAAE,eAAe,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAcvD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAIzD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,eAAe,CAKjD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IACR,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IACpC,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,GACA,OAAO,CAqBT"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Registration Types
|
|
3
|
+
* Local-first project tracking for ClaudeTools
|
|
4
|
+
*/
|
|
5
|
+
import { createHash } from 'node:crypto';
|
|
6
|
+
import { resolve, normalize } from 'node:path';
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
8
|
+
/**
|
|
9
|
+
* Current schema version
|
|
10
|
+
*/
|
|
11
|
+
export const PROJECTS_SCHEMA_VERSION = 1;
|
|
12
|
+
/**
|
|
13
|
+
* Normalize a path for consistent hashing
|
|
14
|
+
* - Resolves to absolute path
|
|
15
|
+
* - Lowercases on case-insensitive systems
|
|
16
|
+
* - Removes trailing slashes
|
|
17
|
+
*/
|
|
18
|
+
export function normalizePath(inputPath) {
|
|
19
|
+
let normalized = resolve(normalize(inputPath));
|
|
20
|
+
// Remove trailing slash (except for root)
|
|
21
|
+
if (normalized.length > 1 && normalized.endsWith('/')) {
|
|
22
|
+
normalized = normalized.slice(0, -1);
|
|
23
|
+
}
|
|
24
|
+
// Lowercase for case-insensitive filesystems (macOS, Windows)
|
|
25
|
+
if (process.platform === 'darwin' || process.platform === 'win32') {
|
|
26
|
+
normalized = normalized.toLowerCase();
|
|
27
|
+
}
|
|
28
|
+
return normalized;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Compute SHA256 hash of a normalized path
|
|
32
|
+
*/
|
|
33
|
+
export function computePathHash(inputPath) {
|
|
34
|
+
const normalized = normalizePath(inputPath);
|
|
35
|
+
const hash = createHash('sha256').update(normalized).digest('hex');
|
|
36
|
+
return `sha256:${hash}`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Generate a new project ID
|
|
40
|
+
*/
|
|
41
|
+
export function generateProjectId() {
|
|
42
|
+
return randomUUID();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Create default project settings
|
|
46
|
+
*/
|
|
47
|
+
export function defaultSettings() {
|
|
48
|
+
return {
|
|
49
|
+
autoSync: true,
|
|
50
|
+
visibility: 'personal',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create a new project with defaults
|
|
55
|
+
*/
|
|
56
|
+
export function createProject(path, name, options) {
|
|
57
|
+
const now = new Date().toISOString();
|
|
58
|
+
return {
|
|
59
|
+
id: generateProjectId(),
|
|
60
|
+
path,
|
|
61
|
+
pathHash: computePathHash(path),
|
|
62
|
+
name,
|
|
63
|
+
description: options?.description,
|
|
64
|
+
teamId: options?.teamId,
|
|
65
|
+
gitRemoteUrl: options?.gitRemoteUrl,
|
|
66
|
+
registeredAt: now,
|
|
67
|
+
updatedAt: now,
|
|
68
|
+
isSynced: false,
|
|
69
|
+
isDeleted: false,
|
|
70
|
+
settings: {
|
|
71
|
+
...defaultSettings(),
|
|
72
|
+
...options?.settings,
|
|
73
|
+
},
|
|
74
|
+
stackProfile: options?.stackProfile,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=types.js.map
|