gipity 1.0.48

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.
Files changed (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +234 -0
  3. package/dist/__tests__/config.test.d.ts +1 -0
  4. package/dist/__tests__/config.test.js +31 -0
  5. package/dist/__tests__/config.test.js.map +1 -0
  6. package/dist/__tests__/sync.test.d.ts +1 -0
  7. package/dist/__tests__/sync.test.js +89 -0
  8. package/dist/__tests__/sync.test.js.map +1 -0
  9. package/dist/__tests__/utils.test.d.ts +1 -0
  10. package/dist/__tests__/utils.test.js +69 -0
  11. package/dist/__tests__/utils.test.js.map +1 -0
  12. package/dist/api.d.ts +13 -0
  13. package/dist/api.js +85 -0
  14. package/dist/api.js.map +1 -0
  15. package/dist/auth.d.ts +12 -0
  16. package/dist/auth.js +90 -0
  17. package/dist/auth.js.map +1 -0
  18. package/dist/coding-guidelines.d.ts +9 -0
  19. package/dist/coding-guidelines.js +52 -0
  20. package/dist/coding-guidelines.js.map +1 -0
  21. package/dist/commands/agent.d.ts +2 -0
  22. package/dist/commands/agent.js +164 -0
  23. package/dist/commands/agent.js.map +1 -0
  24. package/dist/commands/api.d.ts +2 -0
  25. package/dist/commands/api.js +137 -0
  26. package/dist/commands/api.js.map +1 -0
  27. package/dist/commands/audit.d.ts +2 -0
  28. package/dist/commands/audit.js +70 -0
  29. package/dist/commands/audit.js.map +1 -0
  30. package/dist/commands/browser.d.ts +2 -0
  31. package/dist/commands/browser.js +88 -0
  32. package/dist/commands/browser.js.map +1 -0
  33. package/dist/commands/chat.d.ts +2 -0
  34. package/dist/commands/chat.js +68 -0
  35. package/dist/commands/chat.js.map +1 -0
  36. package/dist/commands/checkpoint.d.ts +2 -0
  37. package/dist/commands/checkpoint.js +67 -0
  38. package/dist/commands/checkpoint.js.map +1 -0
  39. package/dist/commands/credits.d.ts +2 -0
  40. package/dist/commands/credits.js +57 -0
  41. package/dist/commands/credits.js.map +1 -0
  42. package/dist/commands/db.d.ts +2 -0
  43. package/dist/commands/db.js +102 -0
  44. package/dist/commands/db.js.map +1 -0
  45. package/dist/commands/deploy.d.ts +2 -0
  46. package/dist/commands/deploy.js +49 -0
  47. package/dist/commands/deploy.js.map +1 -0
  48. package/dist/commands/file.d.ts +2 -0
  49. package/dist/commands/file.js +85 -0
  50. package/dist/commands/file.js.map +1 -0
  51. package/dist/commands/fn.d.ts +2 -0
  52. package/dist/commands/fn.js +87 -0
  53. package/dist/commands/fn.js.map +1 -0
  54. package/dist/commands/init.d.ts +2 -0
  55. package/dist/commands/init.js +107 -0
  56. package/dist/commands/init.js.map +1 -0
  57. package/dist/commands/login.d.ts +2 -0
  58. package/dist/commands/login.js +62 -0
  59. package/dist/commands/login.js.map +1 -0
  60. package/dist/commands/logout.d.ts +2 -0
  61. package/dist/commands/logout.js +14 -0
  62. package/dist/commands/logout.js.map +1 -0
  63. package/dist/commands/logs.d.ts +2 -0
  64. package/dist/commands/logs.js +39 -0
  65. package/dist/commands/logs.js.map +1 -0
  66. package/dist/commands/memory.d.ts +2 -0
  67. package/dist/commands/memory.js +114 -0
  68. package/dist/commands/memory.js.map +1 -0
  69. package/dist/commands/project.d.ts +2 -0
  70. package/dist/commands/project.js +132 -0
  71. package/dist/commands/project.js.map +1 -0
  72. package/dist/commands/push.d.ts +2 -0
  73. package/dist/commands/push.js +34 -0
  74. package/dist/commands/push.js.map +1 -0
  75. package/dist/commands/rbac.d.ts +2 -0
  76. package/dist/commands/rbac.js +89 -0
  77. package/dist/commands/rbac.js.map +1 -0
  78. package/dist/commands/records.d.ts +2 -0
  79. package/dist/commands/records.js +131 -0
  80. package/dist/commands/records.js.map +1 -0
  81. package/dist/commands/sandbox.d.ts +2 -0
  82. package/dist/commands/sandbox.js +53 -0
  83. package/dist/commands/sandbox.js.map +1 -0
  84. package/dist/commands/scaffold.d.ts +2 -0
  85. package/dist/commands/scaffold.js +38 -0
  86. package/dist/commands/scaffold.js.map +1 -0
  87. package/dist/commands/start-cc.d.ts +2 -0
  88. package/dist/commands/start-cc.js +201 -0
  89. package/dist/commands/start-cc.js.map +1 -0
  90. package/dist/commands/status.d.ts +2 -0
  91. package/dist/commands/status.js +43 -0
  92. package/dist/commands/status.js.map +1 -0
  93. package/dist/commands/sync.d.ts +2 -0
  94. package/dist/commands/sync.js +42 -0
  95. package/dist/commands/sync.js.map +1 -0
  96. package/dist/commands/workflow.d.ts +2 -0
  97. package/dist/commands/workflow.js +163 -0
  98. package/dist/commands/workflow.js.map +1 -0
  99. package/dist/config.d.ts +15 -0
  100. package/dist/config.js +76 -0
  101. package/dist/config.js.map +1 -0
  102. package/dist/index.d.ts +2 -0
  103. package/dist/index.js +65 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/setup.d.ts +23 -0
  106. package/dist/setup.js +168 -0
  107. package/dist/setup.js.map +1 -0
  108. package/dist/sync.d.ts +34 -0
  109. package/dist/sync.js +234 -0
  110. package/dist/sync.js.map +1 -0
  111. package/dist/utils.d.ts +10 -0
  112. package/dist/utils.js +57 -0
  113. package/dist/utils.js.map +1 -0
  114. package/hooks/post-write.sh +17 -0
  115. package/hooks/pre-turn.sh +20 -0
  116. package/package.json +29 -0
package/dist/setup.js ADDED
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Shared project setup helpers used by both `init` and `start-cc`.
3
+ */
4
+ import { resolve, join } from 'path';
5
+ import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
6
+ import { CODING_GUIDELINES } from './coding-guidelines.js';
7
+ export const SKILLS_CONTENT = `# Gipity Integration
8
+
9
+ This is a Gipity-powered project. Gipity gives every project its own cloud infrastructure — hosting, databases, file storage, deployment, sandboxed code execution, and a 64-tool AI agent — with zero setup. You write code here, it runs there.
10
+
11
+ **You are the developer.** Write HTML, JS, CSS, Python — whatever the project needs — directly in this directory. Files auto-sync to Gipity's cloud via hooks. There is no local runtime; do NOT run \`npm install\`, \`npm start\`, \`node\`, or \`python\` locally.
12
+
13
+ ## Workflow
14
+
15
+ 1. Write and edit files normally (auto-pushed to Gipity on every save)
16
+ 2. \`gipity deploy dev\` → get a live URL instantly
17
+ 3. \`curl <url>\` or WebFetch to verify
18
+ 4. \`gipity deploy prod\` when ready
19
+
20
+ ## CLI Commands
21
+
22
+ | Command | Purpose |
23
+ |---------|---------|
24
+ | \`gipity scaffold [title]\` | Create web app structure (src/ with HTML, CSS, JS, favicons) |
25
+ | \`gipity deploy [dev\\|prod]\` | Deploy and get live URL |
26
+ | \`gipity sync [up\\|down\\|check]\` | Manual file sync |
27
+ | \`gipity db create <name>\` | Create a project database |
28
+ | \`gipity db query "SQL"\` | Run SQL on project database |
29
+ | \`gipity db list\` | List databases |
30
+ | \`gipity memory list\\|read\\|write\` | Persistent key-value memory |
31
+ | \`gipity api list\\|define\\|get\` | Manage backend API procedures |
32
+ | \`gipity checkpoint list\` | List file snapshots |
33
+ | \`gipity checkpoint restore <id>\` | Restore files to a snapshot (undo) |
34
+ | \`gipity logs fn <name>\` | View function execution logs (errors, timing) |
35
+ | \`gipity browser <url>\` | Inspect a URL: console errors, timing, failed resources |
36
+ | \`gipity status\` | Check project and sync status |
37
+
38
+ All commands support \`--json\` for structured output.
39
+
40
+ ## Processing & Code Execution
41
+
42
+ Do NOT install tools or run heavy processing locally. Gipity has a cloud sandbox accessible via \`gipity chat\` with extensive tools pre-installed:
43
+
44
+ - **Image/video**: ImageMagick, FFmpeg, Graphviz, gnuplot, optipng, gifsicle, potrace, webp, exiftool, mediainfo
45
+ - **Documents**: LibreOffice (headless), pandoc, wkhtmltopdf, ghostscript, qpdf, poppler-utils
46
+ - **Python**: pandas, numpy, matplotlib, scipy, sympy, pillow, openpyxl, python-docx, python-pptx, reportlab, cairosvg, seaborn, qrcode, requests, bs4, Jinja2, faker
47
+ - **Audio**: sox, FFmpeg
48
+ - **Data**: jq, sqlite3, csvkit, datamash, miller, xmlstarlet
49
+ - **Compile**: GCC/G++, mingw-w64 (Windows cross-compile)
50
+
51
+ Use \`gipity chat\` to have the platform do it. Example: \`gipity chat "resize all images in src/images to 800px wide"\`
52
+
53
+ ## File Operations
54
+
55
+ All file creation and editing should happen locally — hooks auto-push changes to Gipity. Do NOT use \`gipity chat\` to create or edit files. Use \`gipity sync\` if files get out of sync. Use \`gipity file ls\` and \`gipity file cat\` to browse remote files without syncing.
56
+
57
+ ## Platform Services (via \`gipity chat\`)
58
+
59
+ Some Gipity capabilities require its hosted AI agent. Use \`gipity chat\` for things you cannot do directly. The response includes text output, which tools were used, and any files created are auto-synced locally. Add \`--json\` for structured output.
60
+
61
+ - **Image generation**: \`gipity chat "generate a hero image for the landing page"\`
62
+ - **Speech / TTS**: \`gipity chat "generate speech audio for this welcome message"\`
63
+ - **Sound effects / Music**: \`gipity chat "generate a click sound effect"\`
64
+ - **Web search**: \`gipity chat "search the web for current pricing of competitor X"\`
65
+ - **Twitter/X search**: \`gipity chat "search twitter for mentions of our app"\`
66
+ - **Browser interaction**: \`gipity chat "open our deployed app, click the login button, and screenshot the result"\`
67
+ - **Workflow automation**: \`gipity chat "create a cron workflow that emails a daily summary"\`
68
+ - **Email**: \`gipity chat "send a summary email to the team"\`
69
+ - **LLM queries**: \`gipity chat "ask GPT-4 to review this code for security issues"\`
70
+
71
+ Do NOT use \`gipity chat\` for writing code, creating files, or scaffolding — do that directly.
72
+
73
+ ${CODING_GUIDELINES}
74
+
75
+ ## Authentication
76
+
77
+ Login is a two-step process that works non-interactively:
78
+
79
+ 1. \`gipity login --email user@example.com\` → sends a 6-digit code to that email
80
+ 2. Ask the user for the code, then: \`gipity login --email user@example.com --code 123456\`
81
+
82
+ Check auth status: \`gipity status\`
83
+ Log out: \`gipity logout\`
84
+
85
+ ## Sync Behavior
86
+
87
+ - **Auto-push**: Files push to Gipity after every Write/Edit (hook)
88
+ - **Auto-pull**: Remote changes pull before each prompt (hook)
89
+ - **Manual**: \`gipity sync check\` to see pending changes
90
+ - **Deletes are safe**: All file deletions are soft deletes. Use \`gipity checkpoint list\` and \`gipity checkpoint restore <id>\` to undo any delete or revert to a previous state.
91
+
92
+ ## Debugging
93
+
94
+ - \`gipity browser <url>\` — open a deployed URL and get JS console errors, failed resources (404s), and page load timing. Useful when something looks broken after deploy.
95
+ - \`gipity logs fn <name>\` — view recent function execution logs with error messages and timing. Use when API calls return errors.
96
+ - \`gipity sync check\` — verify local and remote files are in sync if things seem stale.
97
+ - \`gipity checkpoint restore <id>\` — undo a bad change by restoring to a previous file snapshot.
98
+ `;
99
+ export const HOOKS_SETTINGS = {
100
+ hooks: {
101
+ PostToolUse: [{
102
+ matcher: 'Write|Edit',
103
+ hooks: [{
104
+ type: 'command',
105
+ command: 'bash -c \'INPUT=$(cat); FILE_PATH=$(echo "$INPUT" | jq -r ".tool_input.file_path // empty"); [ -z "$FILE_PATH" ] && exit 0; [ ! -f ".gipity.json" ] && exit 0; gipity push "$FILE_PATH" --quiet & disown; exit 0\'',
106
+ }],
107
+ }],
108
+ UserPromptSubmit: [{
109
+ matcher: '',
110
+ hooks: [{
111
+ type: 'command',
112
+ command: 'bash -c \'[ ! -f ".gipity.json" ] && exit 0; RESULT=$(gipity sync down --json 2>/dev/null); PULLED=$(echo "$RESULT" | jq -r ".pulled // 0" 2>/dev/null); if [ "$PULLED" -gt 0 ]; then SUMMARY=$(echo "$RESULT" | jq -r ".summary // empty" 2>/dev/null); echo "{\\\"systemMessage\\\": \\\"Gipity sync: $SUMMARY\\\"}"; fi; exit 0\'',
113
+ }],
114
+ }],
115
+ },
116
+ };
117
+ export function setupClaudeHooks() {
118
+ const claudeDir = resolve(process.cwd(), '.claude');
119
+ mkdirSync(claudeDir, { recursive: true });
120
+ const settingsPath = join(claudeDir, 'settings.json');
121
+ let settings = {};
122
+ if (existsSync(settingsPath)) {
123
+ try {
124
+ settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
125
+ }
126
+ catch {
127
+ // corrupted — overwrite
128
+ }
129
+ }
130
+ settings.hooks = HOOKS_SETTINGS.hooks;
131
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
132
+ }
133
+ export function setupClaudeMd() {
134
+ const claudeMdPath = resolve(process.cwd(), 'CLAUDE.md');
135
+ if (existsSync(claudeMdPath)) {
136
+ const existing = readFileSync(claudeMdPath, 'utf-8');
137
+ if (existing.includes('Gipity Integration'))
138
+ return;
139
+ writeFileSync(claudeMdPath, existing + '\n\n' + SKILLS_CONTENT);
140
+ }
141
+ else {
142
+ writeFileSync(claudeMdPath, SKILLS_CONTENT);
143
+ }
144
+ }
145
+ export function setupGitignore() {
146
+ const gitignorePath = resolve(process.cwd(), '.gitignore');
147
+ const entries = ['.gipity/', '.gipity.json'];
148
+ if (existsSync(gitignorePath)) {
149
+ let content = readFileSync(gitignorePath, 'utf-8');
150
+ const lines = content.split('\n');
151
+ const toAdd = entries.filter(e => !lines.includes(e));
152
+ if (toAdd.length > 0) {
153
+ content = content.trimEnd() + '\n' + toAdd.join('\n') + '\n';
154
+ writeFileSync(gitignorePath, content);
155
+ }
156
+ }
157
+ else {
158
+ writeFileSync(gitignorePath, entries.join('\n') + '\n');
159
+ }
160
+ }
161
+ export function slugify(name) {
162
+ return name
163
+ .toLowerCase()
164
+ .replace(/[^a-z0-9-]/g, '-')
165
+ .replace(/-+/g, '-')
166
+ .replace(/^-|-$/g, '');
167
+ }
168
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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"}
package/dist/sync.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ interface RemoteFile {
2
+ path: string;
3
+ size: number;
4
+ modified: string;
5
+ type: string;
6
+ guid: string;
7
+ }
8
+ export interface SyncChange {
9
+ type: 'added' | 'modified' | 'deleted';
10
+ path: string;
11
+ localSize?: number;
12
+ remoteSize?: number;
13
+ }
14
+ export interface SyncResult {
15
+ changes: SyncChange[];
16
+ pulled: number;
17
+ pushed: number;
18
+ summary: string;
19
+ }
20
+ /** Compare remote manifest against local files, detect changes */
21
+ export declare function diffManifest(remoteFiles: RemoteFile[], localFiles: Map<string, {
22
+ size: number;
23
+ modified: string;
24
+ }>, direction: 'down' | 'up'): SyncChange[];
25
+ export declare function formatDiff(changes: SyncChange[], direction: 'down' | 'up'): string;
26
+ /** Sync down: pull remote changes to local */
27
+ export declare function syncDown(): Promise<SyncResult>;
28
+ /** Sync up: push local changes to remote */
29
+ export declare function syncUp(): Promise<SyncResult>;
30
+ /** Check for changes without pulling/pushing */
31
+ export declare function syncCheck(): Promise<SyncResult>;
32
+ /** Push a single file to remote */
33
+ export declare function pushFile(filePath: string): Promise<void>;
34
+ export {};
package/dist/sync.js ADDED
@@ -0,0 +1,234 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, statSync, unlinkSync, readdirSync } from 'fs';
2
+ import { join, relative, dirname } from 'path';
3
+ import { get, put, del } from './api.js';
4
+ import { requireConfig, shouldIgnore, getConfigPath } from './config.js';
5
+ import { isBinaryFile, formatSize } from './utils.js';
6
+ function syncStatePath() {
7
+ const configPath = getConfigPath();
8
+ const projectDir = dirname(configPath);
9
+ return join(projectDir, '.gipity', 'sync-state.json');
10
+ }
11
+ function projectDir() {
12
+ const configPath = getConfigPath();
13
+ return dirname(configPath);
14
+ }
15
+ function loadSyncState() {
16
+ const path = syncStatePath();
17
+ if (!existsSync(path))
18
+ return { lastSync: '', files: {} };
19
+ try {
20
+ return JSON.parse(readFileSync(path, 'utf-8'));
21
+ }
22
+ catch {
23
+ return { lastSync: '', files: {} };
24
+ }
25
+ }
26
+ function saveSyncState(state) {
27
+ const path = syncStatePath();
28
+ mkdirSync(dirname(path), { recursive: true });
29
+ writeFileSync(path, JSON.stringify(state, null, 2));
30
+ }
31
+ /** Walk local directory, returning relative paths and sizes */
32
+ function walkLocal(dir, base, ignorePatterns) {
33
+ const result = new Map();
34
+ function walk(currentDir) {
35
+ let entries;
36
+ try {
37
+ entries = readdirSync(currentDir, { withFileTypes: true });
38
+ }
39
+ catch {
40
+ return;
41
+ }
42
+ for (const entry of entries) {
43
+ const fullPath = join(currentDir, entry.name);
44
+ const relPath = relative(base, fullPath).replace(/\\/g, '/');
45
+ if (shouldIgnore(relPath, ignorePatterns))
46
+ continue;
47
+ if (entry.isDirectory()) {
48
+ walk(fullPath);
49
+ }
50
+ else if (entry.isFile()) {
51
+ try {
52
+ const stat = statSync(fullPath);
53
+ result.set(relPath, {
54
+ size: stat.size,
55
+ modified: stat.mtime.toISOString(),
56
+ });
57
+ }
58
+ catch {
59
+ // skip unreadable files
60
+ }
61
+ }
62
+ }
63
+ }
64
+ walk(dir);
65
+ return result;
66
+ }
67
+ /** Fetch remote file manifest */
68
+ async function fetchManifest(projectGuid, prefix) {
69
+ const query = prefix ? `?path=${encodeURIComponent(prefix)}` : '';
70
+ const res = await get(`/projects/${projectGuid}/files/tree${query}`);
71
+ return res.data;
72
+ }
73
+ /** Compare remote manifest against local files, detect changes */
74
+ export function diffManifest(remoteFiles, localFiles, direction) {
75
+ const changes = [];
76
+ if (direction === 'down') {
77
+ // Detect remote changes: added/modified remotely, deleted remotely
78
+ const remoteMap = new Map(remoteFiles.filter(f => f.type === 'file').map(f => [f.path, f]));
79
+ for (const [path, remote] of remoteMap) {
80
+ const local = localFiles.get(path);
81
+ if (!local) {
82
+ changes.push({ type: 'added', path, remoteSize: remote.size });
83
+ }
84
+ else if (local.size !== remote.size) {
85
+ changes.push({ type: 'modified', path, localSize: local.size, remoteSize: remote.size });
86
+ }
87
+ }
88
+ // Files that exist locally but not remotely → deleted remotely
89
+ for (const [path] of localFiles) {
90
+ if (!remoteMap.has(path)) {
91
+ changes.push({ type: 'deleted', path, localSize: localFiles.get(path).size });
92
+ }
93
+ }
94
+ }
95
+ else {
96
+ // Detect local changes: added/modified locally, deleted locally
97
+ const remoteMap = new Map(remoteFiles.filter(f => f.type === 'file').map(f => [f.path, f]));
98
+ for (const [path, local] of localFiles) {
99
+ const remote = remoteMap.get(path);
100
+ if (!remote) {
101
+ changes.push({ type: 'added', path, localSize: local.size });
102
+ }
103
+ else if (local.size !== remote.size) {
104
+ changes.push({ type: 'modified', path, localSize: local.size, remoteSize: remote.size });
105
+ }
106
+ }
107
+ // Files that exist remotely but not locally → deleted locally
108
+ for (const [path, remote] of remoteMap) {
109
+ if (!localFiles.has(path)) {
110
+ changes.push({ type: 'deleted', path, remoteSize: remote.size });
111
+ }
112
+ }
113
+ }
114
+ return changes;
115
+ }
116
+ export function formatDiff(changes, direction) {
117
+ if (changes.length === 0)
118
+ return 'No changes detected.';
119
+ const label = direction === 'down' ? 'remotely' : 'locally';
120
+ const lines = [`${changes.length} change${changes.length > 1 ? 's' : ''}:`];
121
+ for (const c of changes) {
122
+ switch (c.type) {
123
+ case 'added':
124
+ lines.push(` + ${c.path} (new, ${formatSize(c.remoteSize || c.localSize || 0)})`);
125
+ break;
126
+ case 'modified':
127
+ lines.push(` ~ ${c.path} (${formatSize(c.localSize || 0)} → ${formatSize(c.remoteSize || 0)})`);
128
+ break;
129
+ case 'deleted':
130
+ lines.push(` - ${c.path} (deleted ${label})`);
131
+ break;
132
+ }
133
+ }
134
+ return lines.join('\n');
135
+ }
136
+ /** Sync down: pull remote changes to local */
137
+ export async function syncDown() {
138
+ const config = requireConfig();
139
+ const root = projectDir();
140
+ const remoteFiles = await fetchManifest(config.projectGuid);
141
+ const localFiles = walkLocal(root, root, config.ignore);
142
+ const changes = diffManifest(remoteFiles, localFiles, 'down');
143
+ let pulled = 0;
144
+ for (const change of changes) {
145
+ if (change.type === 'deleted') {
146
+ // File deleted remotely — delete locally
147
+ const fullPath = join(root, change.path);
148
+ try {
149
+ unlinkSync(fullPath);
150
+ }
151
+ catch { /* already gone */ }
152
+ pulled++;
153
+ continue;
154
+ }
155
+ // added or modified — download from remote
156
+ const res = await get(`/projects/${config.projectGuid}/files/read?path=${encodeURIComponent(change.path)}`);
157
+ const fullPath = join(root, change.path);
158
+ mkdirSync(dirname(fullPath), { recursive: true });
159
+ writeFileSync(fullPath, res.data.content);
160
+ pulled++;
161
+ }
162
+ // Update sync state
163
+ const updatedLocal = walkLocal(root, root, config.ignore);
164
+ const stateFiles = {};
165
+ for (const [path, info] of updatedLocal) {
166
+ stateFiles[path] = info;
167
+ }
168
+ saveSyncState({ lastSync: new Date().toISOString(), files: stateFiles });
169
+ const summary = formatDiff(changes, 'down');
170
+ return { changes, pulled, pushed: 0, summary };
171
+ }
172
+ /** Sync up: push local changes to remote */
173
+ export async function syncUp() {
174
+ const config = requireConfig();
175
+ const root = projectDir();
176
+ const remoteFiles = await fetchManifest(config.projectGuid);
177
+ const localFiles = walkLocal(root, root, config.ignore);
178
+ const changes = diffManifest(remoteFiles, localFiles, 'up');
179
+ let pushed = 0;
180
+ for (const change of changes) {
181
+ if (change.type === 'deleted') {
182
+ // File deleted locally — delete on remote
183
+ try {
184
+ await del(`/projects/${config.projectGuid}/files?path=${encodeURIComponent(change.path)}`);
185
+ pushed++;
186
+ }
187
+ catch {
188
+ // Remote file may already be gone
189
+ }
190
+ continue;
191
+ }
192
+ // added or modified — push to remote (skip binary files)
193
+ const fullPath = join(root, change.path);
194
+ const raw = readFileSync(fullPath);
195
+ if (isBinaryFile(raw))
196
+ continue;
197
+ const content = raw.toString('utf-8');
198
+ await put(`/projects/${config.projectGuid}/files`, { path: change.path, content });
199
+ pushed++;
200
+ }
201
+ // Update sync state
202
+ const updatedLocal = walkLocal(root, root, config.ignore);
203
+ const stateFiles = {};
204
+ for (const [path, info] of updatedLocal) {
205
+ stateFiles[path] = info;
206
+ }
207
+ saveSyncState({ lastSync: new Date().toISOString(), files: stateFiles });
208
+ const summary = formatDiff(changes.filter(c => c.type !== 'deleted'), 'up');
209
+ return { changes, pushed, pulled: 0, summary };
210
+ }
211
+ /** Check for changes without pulling/pushing */
212
+ export async function syncCheck() {
213
+ const config = requireConfig();
214
+ const root = projectDir();
215
+ const remoteFiles = await fetchManifest(config.projectGuid);
216
+ const localFiles = walkLocal(root, root, config.ignore);
217
+ const downChanges = diffManifest(remoteFiles, localFiles, 'down');
218
+ const summary = formatDiff(downChanges, 'down');
219
+ return { changes: downChanges, pulled: 0, pushed: 0, summary };
220
+ }
221
+ /** Push a single file to remote */
222
+ export async function pushFile(filePath) {
223
+ const config = requireConfig();
224
+ const root = projectDir();
225
+ const relPath = relative(root, filePath).replace(/\\/g, '/');
226
+ if (shouldIgnore(relPath, config.ignore))
227
+ return;
228
+ const raw = readFileSync(filePath);
229
+ if (isBinaryFile(raw))
230
+ return;
231
+ const content = raw.toString('utf-8');
232
+ await put(`/projects/${config.projectGuid}/files`, { path: relPath, content });
233
+ }
234
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AAC3G,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/C,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AA6BtD,SAAS,aAAa;IACpB,MAAM,UAAU,GAAG,aAAa,EAAG,CAAC;IACpC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACvC,OAAO,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,iBAAiB,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,UAAU;IACjB,MAAM,UAAU,GAAG,aAAa,EAAG,CAAC;IACpC,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,aAAa;IACpB,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;IAC7B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAC1D,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACrC,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,KAAgB;IACrC,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;IAC7B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,+DAA+D;AAC/D,SAAS,SAAS,CAAC,GAAW,EAAE,IAAY,EAAE,cAAwB;IACpE,MAAM,MAAM,GAAG,IAAI,GAAG,EAA8C,CAAC;IAErE,SAAS,IAAI,CAAC,UAAkB;QAC9B,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAE7D,IAAI,YAAY,CAAC,OAAO,EAAE,cAAc,CAAC;gBAAE,SAAS;YAEpD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,IAAI,CAAC,QAAQ,CAAC,CAAC;YACjB,CAAC;iBAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;gBAC1B,IAAI,CAAC;oBACH,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;oBAChC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE;wBAClB,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;qBACnC,CAAC,CAAC;gBACL,CAAC;gBAAC,MAAM,CAAC;oBACP,wBAAwB;gBAC1B,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,iCAAiC;AACjC,KAAK,UAAU,aAAa,CAAC,WAAmB,EAAE,MAAe;IAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,SAAS,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAClE,MAAM,GAAG,GAAG,MAAM,GAAG,CAAyB,aAAa,WAAW,cAAc,KAAK,EAAE,CAAC,CAAC;IAC7F,OAAO,GAAG,CAAC,IAAI,CAAC;AAClB,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,YAAY,CAC1B,WAAyB,EACzB,UAA2D,EAC3D,SAAwB;IAExB,MAAM,OAAO,GAAiB,EAAE,CAAC;IAEjC,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;QACzB,mEAAmE;QACnE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAE5F,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YACvC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YACjE,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC;gBACtC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YAC3F,CAAC;QACH,CAAC;QAED,+DAA+D;QAC/D,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,UAAU,EAAE,CAAC;YAChC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,IAAI,EAAE,CAAC,CAAC;YACjF,CAAC;QACH,CAAC;IACH,CAAC;SAAM,CAAC;QACN,gEAAgE;QAChE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAE5F,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,UAAU,EAAE,CAAC;YACvC,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YAC/D,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC;gBACtC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YAC3F,CAAC;QACH,CAAC;QAED,8DAA8D;QAC9D,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YACvC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1B,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YACnE,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAqB,EAAE,SAAwB;IACxE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,sBAAsB,CAAC;IAExD,MAAM,KAAK,GAAG,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5D,MAAM,KAAK,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,UAAU,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAE5E,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;YACf,KAAK,OAAO;gBACV,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,UAAU,UAAU,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;gBACnF,MAAM;YACR,KAAK,UAAU;gBACb,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;gBACjG,MAAM;YACR,KAAK,SAAS;gBACZ,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,aAAa,KAAK,GAAG,CAAC,CAAC;gBAC/C,MAAM;QACV,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,8CAA8C;AAC9C,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,MAAM,WAAW,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC5D,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;IAE9D,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC9B,yCAAyC;YACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,CAAC;gBAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;YAC1D,MAAM,EAAE,CAAC;YACT,SAAS;QACX,CAAC;QAED,2CAA2C;QAC3C,MAAM,GAAG,GAAG,MAAM,GAAG,CACnB,aAAa,MAAM,CAAC,WAAW,oBAAoB,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CACrF,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACzC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,aAAa,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,EAAE,CAAC;IACX,CAAC;IAED,oBAAoB;IACpB,MAAM,YAAY,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAuD,EAAE,CAAC;IAC1E,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,YAAY,EAAE,CAAC;QACxC,UAAU,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1B,CAAC;IACD,aAAa,CAAC,EAAE,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IAEzE,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC;AACjD,CAAC;AAED,4CAA4C;AAC5C,MAAM,CAAC,KAAK,UAAU,MAAM;IAC1B,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,MAAM,WAAW,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC5D,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;IAE5D,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC9B,0CAA0C;YAC1C,IAAI,CAAC;gBACH,MAAM,GAAG,CAAC,aAAa,MAAM,CAAC,WAAW,eAAe,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC3F,MAAM,EAAE,CAAC;YACX,CAAC;YAAC,MAAM,CAAC;gBACP,kCAAkC;YACpC,CAAC;YACD,SAAS;QACX,CAAC;QAED,yDAAyD;QACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,YAAY,CAAC,GAAG,CAAC;YAAE,SAAS;QAChC,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,GAAG,CAAC,aAAa,MAAM,CAAC,WAAW,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QACnF,MAAM,EAAE,CAAC;IACX,CAAC;IAED,oBAAoB;IACpB,MAAM,YAAY,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAuD,EAAE,CAAC;IAC1E,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,YAAY,EAAE,CAAC;QACxC,UAAU,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1B,CAAC;IACD,aAAa,CAAC,EAAE,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IAEzE,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;IAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC;AACjD,CAAC;AAED,gDAAgD;AAChD,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,MAAM,WAAW,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC5D,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAExD,MAAM,WAAW,GAAG,YAAY,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAEhD,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC;AACjE,CAAC;AAED,mCAAmC;AACnC,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,QAAgB;IAC7C,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAE7D,IAAI,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC;QAAE,OAAO;IAEjD,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,YAAY,CAAC,GAAG,CAAC;QAAE,OAAO;IAC9B,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,GAAG,CAAC,aAAa,MAAM,CAAC,WAAW,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AACjF,CAAC"}
@@ -0,0 +1,10 @@
1
+ /** Safely decode a JWT payload without signature validation */
2
+ export declare function decodeJwtExp(token: string): number | null;
3
+ /** Prompt the user for input on stdin */
4
+ export declare function prompt(question: string): Promise<string>;
5
+ /** Check if a file is likely binary by reading its first bytes */
6
+ export declare function isBinaryFile(buffer: Buffer): boolean;
7
+ /** Format an ISO timestamp as a relative age string */
8
+ export declare function formatAge(iso: string): string;
9
+ /** Format byte count as human-readable string */
10
+ export declare function formatSize(bytes: number): string;
package/dist/utils.js ADDED
@@ -0,0 +1,57 @@
1
+ import { createInterface } from 'readline';
2
+ /** Safely decode a JWT payload without signature validation */
3
+ export function decodeJwtExp(token) {
4
+ try {
5
+ const parts = token.split('.');
6
+ if (parts.length !== 3)
7
+ return null;
8
+ const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString());
9
+ return typeof payload.exp === 'number' ? payload.exp : null;
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ }
15
+ /** Prompt the user for input on stdin */
16
+ export function prompt(question) {
17
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
18
+ return new Promise(resolve => {
19
+ rl.question(question, answer => {
20
+ rl.close();
21
+ resolve(answer.trim());
22
+ });
23
+ });
24
+ }
25
+ /** Check if a file is likely binary by reading its first bytes */
26
+ export function isBinaryFile(buffer) {
27
+ // Check for null bytes in first 8KB — reliable binary indicator
28
+ const len = Math.min(buffer.length, 8192);
29
+ for (let i = 0; i < len; i++) {
30
+ if (buffer[i] === 0)
31
+ return true;
32
+ }
33
+ return false;
34
+ }
35
+ /** Format an ISO timestamp as a relative age string */
36
+ export function formatAge(iso) {
37
+ const ms = Date.now() - new Date(iso).getTime();
38
+ const mins = Math.floor(ms / 60000);
39
+ if (mins < 1)
40
+ return 'just now';
41
+ if (mins < 60)
42
+ return `${mins}m ago`;
43
+ const hrs = Math.floor(mins / 60);
44
+ if (hrs < 24)
45
+ return `${hrs}h ago`;
46
+ const days = Math.floor(hrs / 24);
47
+ return `${days}d ago`;
48
+ }
49
+ /** Format byte count as human-readable string */
50
+ export function formatSize(bytes) {
51
+ if (bytes < 1024)
52
+ return `${bytes} B`;
53
+ if (bytes < 1024 * 1024)
54
+ return `${(bytes / 1024).toFixed(1)} KB`;
55
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
56
+ }
57
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ #!/bin/bash
2
+ # PostToolUse hook: push file to Gipity after Write/Edit
3
+ # Runs after CC's Write or Edit tool. Fires gipity push in background.
4
+
5
+ INPUT=$(cat)
6
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
7
+
8
+ # Skip if no file path extracted
9
+ [ -z "$FILE_PATH" ] && exit 0
10
+
11
+ # Skip if not a Gipity project
12
+ [ ! -f ".gipity.json" ] && exit 0
13
+
14
+ # Push in background, suppress output
15
+ gipity push "$FILE_PATH" --quiet &
16
+ disown
17
+ exit 0
@@ -0,0 +1,20 @@
1
+ #!/bin/bash
2
+ # UserPromptSubmit hook: sync check before each CC turn
3
+ # Pulls remote changes and reports diff as systemMessage.
4
+
5
+ # Skip if not a Gipity project
6
+ [ ! -f ".gipity.json" ] && exit 0
7
+
8
+ # Sync down with JSON output
9
+ RESULT=$(gipity sync down --json 2>/dev/null)
10
+
11
+ # Check if any files were pulled
12
+ PULLED=$(echo "$RESULT" | jq -r '.pulled // 0' 2>/dev/null)
13
+
14
+ if [ "$PULLED" -gt 0 ]; then
15
+ SUMMARY=$(echo "$RESULT" | jq -r '.summary // "Files changed remotely."' 2>/dev/null)
16
+ # Output systemMessage so CC sees the diff
17
+ echo "{\"systemMessage\": \"Gipity sync: ${SUMMARY}\"}"
18
+ fi
19
+
20
+ exit 0
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "gipity",
3
+ "version": "1.0.48",
4
+ "description": "Connect AI coding agents like Claude Code to the Gipity platform",
5
+ "bin": {
6
+ "gipity": "./dist/index.js"
7
+ },
8
+ "type": "module",
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsc --watch",
12
+ "test": "tsc && node --test dist/__tests__/*.test.js"
13
+ },
14
+ "dependencies": {
15
+ "commander": "^13.0.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^20.0.0",
19
+ "typescript": "^5.5.0"
20
+ },
21
+ "engines": {
22
+ "node": ">=18"
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "hooks"
27
+ ],
28
+ "license": "MIT"
29
+ }