gipity 1.0.406 → 1.0.408

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/banner.js CHANGED
@@ -1,6 +1,21 @@
1
1
  // ── Gipity CLI Startup Banner ──────────────────────────────────────────
2
2
  // Two-panel box showing all AI models, platform tools, and sandbox capabilities.
3
+ import { homedir } from 'os';
4
+ import { sep } from 'path';
3
5
  import { brand, bold, faint, muted, fg } from './colors.js';
6
+ /** Abbreviate a home-relative path to `~`, cross-platform. On Windows `HOME`
7
+ * is usually unset (it's `USERPROFILE`), and `str.replace('', '~')` prepends
8
+ * a stray `~`, so use os.homedir() and a real prefix check. */
9
+ function tildify(p) {
10
+ const home = homedir();
11
+ if (!home)
12
+ return p;
13
+ if (p === home)
14
+ return '~';
15
+ if (p.startsWith(home + sep))
16
+ return '~' + p.slice(home.length);
17
+ return p;
18
+ }
4
19
  // ── Static content ─────────────────────────────────────────────────────
5
20
  // ── Feature groups ────────────────────────────────────────────────────
6
21
  const AI_MODELS = [
@@ -174,7 +189,7 @@ function buildLeftPanel(opts, panelW) {
174
189
  leftLines.push(center(line, panelW));
175
190
  }
176
191
  if (opts.cwd) {
177
- const short = leadingEllipsify(opts.cwd.replace(process.env['HOME'] || '', '~'), panelW);
192
+ const short = leadingEllipsify(tildify(opts.cwd), panelW);
178
193
  leftLines.push('');
179
194
  leftLines.push(center(muted(short), panelW));
180
195
  leftLines.push('');
@@ -1,10 +1,9 @@
1
1
  import { Command } from 'commander';
2
- import { join, dirname, resolve, basename } from 'path';
2
+ import { join, dirname, resolve, basename, sep } from 'path';
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync, readdirSync, statSync } from 'fs';
4
- import { spawn } from 'child_process';
5
4
  import { homedir } from 'os';
6
5
  import { fileURLToPath } from 'url';
7
- import { resolveCommand } from '../platform.js';
6
+ import { resolveCommand, spawnCommand } from '../platform.js';
8
7
  import { getAuth, saveAuth, sessionExpired, accessTokenExpired, refreshTokenIfNeeded } from '../auth.js';
9
8
  import { get, post, publicPost, ApiError, getAccountSlug } from '../api.js';
10
9
  import { getConfig, saveConfigAt, clearConfigCache, getApiBaseOverride, DEFAULT_API_BASE, getConfigPath } from '../config.js';
@@ -234,7 +233,7 @@ export const claudeCommand = new Command('claude')
234
233
  // Forwarded to `claude` via the unknown-arg passthrough below (NOT in the
235
234
  // gipity strip lists). Declared here only so it shows up in --help — without
236
235
  // this, callers can't discover that the session model is selectable.
237
- .option('--model <model>', 'Model for the Claude session, forwarded to claude (e.g. sonnet, opus, or a full id like claude-sonnet-4-6)')
236
+ .option('--model <model>', 'Model for the Claude session, forwarded to claude (e.g. sonnet, opus, or a full id like claude-sonnet-5)')
238
237
  .allowUnknownOption(true)
239
238
  .allowExcessArguments(true)
240
239
  .action(async (opts) => {
@@ -822,8 +821,9 @@ export const claudeCommand = new Command('claude')
822
821
  allArgs.push('--verbose');
823
822
  }
824
823
  }
825
- // Resolve full path on Windows so we can avoid shell:true, which
826
- // passes args through cmd.exe and mangles quotes/special chars.
824
+ // Resolve the real path on Windows; spawnCommand then routes the `.cmd`
825
+ // shim through cmd.exe with verbatim args (a bare `spawn` of a `.cmd`
826
+ // throws EINVAL on Node >=18.20.2/20.12.2 - the CVE-2024-27980 fix).
827
827
  const claudeCmd = resolveCommand('claude');
828
828
  const childEnv = { ...process.env };
829
829
  // Gate hook-based capture: when the daemon is streaming via
@@ -844,7 +844,7 @@ export const claudeCommand = new Command('claude')
844
844
  // `-p` runs skip the dialog already, so this is only needed interactively.
845
845
  if (!nonInteractive)
846
846
  markFolderTrusted(process.cwd());
847
- const child = spawn(claudeCmd, allArgs, {
847
+ const child = spawnCommand(claudeCmd, allArgs, {
848
848
  stdio: 'inherit',
849
849
  cwd: process.cwd(),
850
850
  env: childEnv,
@@ -945,10 +945,10 @@ function formatNewProjectLabel(existingSlugs) {
945
945
  const slug = suggestProjectName(existingSlugs);
946
946
  const root = getProjectsRoot();
947
947
  const home = homedir();
948
- const display = root.startsWith(home + '/') || root === home
948
+ const display = root === home || root.startsWith(home + sep)
949
949
  ? '~' + root.slice(home.length)
950
950
  : root;
951
- return `${display}/${slug}`;
951
+ return `${display}${sep}${slug}`;
952
952
  }
953
953
  /** Run the size-tier scan, surface the right UX:
954
954
  * - easy → silent, return true.
@@ -190,6 +190,11 @@ GCC/Rust).
190
190
  if (res.data.autoMirrorSkipped) {
191
191
  console.error(dim(`Note: ${res.data.autoMirrorSkipped.reason}`));
192
192
  }
193
+ if (res.data.mirrorWarnings && res.data.mirrorWarnings.length > 0) {
194
+ console.error(dim(`Note: ${res.data.mirrorWarnings.length} project file(s) could not be mirrored into the sandbox and were skipped:`));
195
+ for (const w of res.data.mirrorWarnings)
196
+ console.error(dim(` - ${w}`));
197
+ }
193
198
  if (res.data.stdout)
194
199
  console.log(res.data.stdout);
195
200
  if (res.data.stderr)
@@ -168,6 +168,12 @@ export const testCommand = new Command('test')
168
168
  console.log(clrError(`No tests matched filter: ${filterPath}`));
169
169
  process.exit(1);
170
170
  }
171
+ if (!filterPath && data.total === 0 && data.results.length === 0) {
172
+ console.log(muted('No tests ran - this app has no tests/*.test.js files.'));
173
+ console.log(muted('gipity test runs server-function tests only; a frontend-only app (no functions/) has nothing here to run.'));
174
+ console.log(muted('Verify a frontend app by driving the live page: gipity page inspect <url> (or gipity page eval).'));
175
+ return;
176
+ }
171
177
  if (data.status === 'failed' && data.errorMessage) {
172
178
  console.log(clrError(`Run failed: ${data.errorMessage}`));
173
179
  console.log('');
package/dist/knowledge.js CHANGED
@@ -155,8 +155,10 @@ App services skills (load before calling \`/services/*\` endpoints):
155
155
 
156
156
  App development skills:
157
157
  - \`agent-deploy\` - headless auth via agent API tokens (GIPITY_TOKEN) for unattended deploys
158
+ - \`app-database\` - app Postgres database: migrations, the db helper, transactions, table permissions
158
159
  - \`app-debugging\` - debug a deployed app: page inspect/eval, screenshots, function logs
159
160
  - \`app-development\` - functions, database, and API
161
+ - \`app-testing\` - testing deployed app functions (ctx.fn.call/callAs, the isolated test DB)
160
162
  - \`deploy\` - the deploy pipeline & gipity.yaml manifest
161
163
  - \`jobs\` - long-running CPU + GPU compute jobs (Python / Node / bash)
162
164
  - \`realtime-scheduled-app\` - recipe: realtime presence/messages + DB function + scheduled poster, end-to-end
package/dist/platform.js CHANGED
@@ -1,12 +1,16 @@
1
- import { execSync } from 'child_process';
1
+ import { execSync, spawn, spawnSync } from 'child_process';
2
2
  /**
3
- * Resolve a command to something Node's spawn/spawnSync can launch directly.
3
+ * Resolve a command to a concrete path when possible.
4
4
  *
5
5
  * On Windows, npm-installed CLIs (npm, npx, gipity, claude) are `.cmd` batch
6
- * shims, not `.exe`s. Node's spawn without `shell: true` cannot launch a bare
7
- * `npm` - it fails with ENOENT and a null exit status. Resolving the real path
8
- * (preferring `.exe`, falling back to `.cmd`) lets us spawn without `shell: true`
9
- * (which would otherwise require escaping arguments).
6
+ * shims, not `.exe`s. A bare `npm` fails to spawn with ENOENT, so we resolve
7
+ * the real path (preferring a native `.exe`, falling back to the `.cmd` shim).
8
+ *
9
+ * NOTE: resolving to the `.cmd` path is NOT enough on its own. Since Node
10
+ * 18.20.2 / 20.12.2 (the CVE-2024-27980 fix), `spawn`/`spawnSync` REFUSE to
11
+ * launch a `.cmd`/`.bat` target unless `shell: true` - they throw `EINVAL`.
12
+ * So always launch a resolved command through `spawnCommand` /
13
+ * `spawnSyncCommand` below, which route batch shims through cmd.exe safely.
10
14
  */
11
15
  export function resolveCommand(cmd) {
12
16
  if (process.platform !== 'win32')
@@ -23,4 +27,47 @@ export function resolveCommand(cmd) {
23
27
  return `${cmd}.cmd`;
24
28
  }
25
29
  }
30
+ /** True for a Windows batch shim that Node cannot spawn without a shell. */
31
+ export function isBatchShim(cmd) {
32
+ return process.platform === 'win32' && /\.(cmd|bat)$/i.test(cmd);
33
+ }
34
+ /**
35
+ * Build a cmd.exe invocation that launches a `.cmd`/`.bat` shim with its args
36
+ * passed through verbatim - the correct way to run a batch file post-CVE
37
+ * without `shell: true` mangling argument quoting.
38
+ *
39
+ * `cmd.exe /d /s /c "<line>"`: `/d` skips AutoRun, `/s` + the wrapping outer
40
+ * quotes make cmd strip exactly the first and last quote of the whole line
41
+ * (leaving each already-quoted token intact). We quote every token ourselves
42
+ * and set `windowsVerbatimArguments` so Node hands our line to cmd untouched.
43
+ */
44
+ export function winBatchInvocation(cmd, args) {
45
+ const comspec = process.env.ComSpec || 'cmd.exe';
46
+ // Double-quote each token; escape any embedded double-quote as "" (cmd's
47
+ // in-quote escape). Our call sites pass flags/paths/guids with no cmd
48
+ // metacharacters, so this conservative quoting is sufficient and safe.
49
+ const quote = (t) => `"${String(t).replace(/"/g, '""')}"`;
50
+ const line = [cmd, ...args].map(quote).join(' ');
51
+ return { file: comspec, args: ['/d', '/s', '/c', `"${line}"`] };
52
+ }
53
+ /**
54
+ * Drop-in `spawn` that survives Windows batch shims. When `cmd` is a `.cmd`/
55
+ * `.bat` on win32, it is launched via cmd.exe with verbatim arguments;
56
+ * otherwise this is a plain `spawn`. Use for anything from `resolveCommand`.
57
+ */
58
+ export function spawnCommand(cmd, args = [], options = {}) {
59
+ if (isBatchShim(cmd)) {
60
+ const inv = winBatchInvocation(cmd, args);
61
+ return spawn(inv.file, inv.args, { ...options, windowsVerbatimArguments: true });
62
+ }
63
+ return spawn(cmd, [...args], options);
64
+ }
65
+ /** Drop-in `spawnSync` counterpart of {@link spawnCommand}. */
66
+ export function spawnSyncCommand(cmd, args = [], options = {}) {
67
+ if (isBatchShim(cmd)) {
68
+ const inv = winBatchInvocation(cmd, args);
69
+ return spawnSync(inv.file, inv.args, { ...options, windowsVerbatimArguments: true });
70
+ }
71
+ return spawnSync(cmd, [...args], options);
72
+ }
26
73
  //# sourceMappingURL=platform.js.map
@@ -1,27 +1,4 @@
1
- /**
2
- * Gipity relay daemon - the `gipity-relay` long-running helper that backs
3
- * `gipity relay run`. Runs two concurrent loops against the paired Gipity
4
- * account using the device's bearer token:
5
- *
6
- * 1. Heartbeat every 60s → POST /remote-devices/heartbeat. Drives the
7
- * web CLI's presence indicator.
8
- * 2. Long-poll → GET /remote-devices/next. On a 200 claim, look up the
9
- * dispatch's project in the local allowlist, spawn `gipity claude -p
10
- * "<msg>"` in that project's cwd, wait for it to exit, POST ack.
11
- *
12
- * The conversation stream (prompts, tool calls, assistant output) flows
13
- * back to the web CLI *automatically* via the capture hooks installed in
14
- * `.claude/settings.json` - the daemon itself doesn't forward content.
15
- *
16
- * Graceful exit:
17
- * - SIGINT / SIGTERM → stop both loops, wait for in-flight child, exit 0.
18
- * - 401 from heartbeat or /next → device was revoked; exit 0.
19
- * - Any other backend error → log and retry with exponential backoff.
20
- *
21
- * See docs/feature-backlog/gipity-relay-phases.md (Phase A Step 7).
22
- */
23
- import { spawn } from 'child_process';
24
- import { resolveCommand } from '../platform.js';
1
+ import { resolveCommand, spawnCommand } from '../platform.js';
25
2
  import { appendFileSync, mkdirSync, existsSync, readFileSync, writeFileSync, chmodSync, closeSync, openSync, unlinkSync } from 'fs';
26
3
  import { stat, readFile } from 'fs/promises';
27
4
  import { createInterface } from 'readline';
@@ -528,14 +505,15 @@ function isSafeSessionId(s) {
528
505
  return /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(s);
529
506
  }
530
507
  /** Resolve Claude Code's on-disk transcript path for measuring resume
531
- * size. Claude encodes the project cwd into a slug by replacing `/` with
532
- * `-`. We only read this file cosmetically (to show "resume 5 KB" in the
533
- * Invoking marker); actual capture is via stream-json. Returns null for
508
+ * size. Claude encodes the project cwd into a slug by replacing every
509
+ * non-alphanumeric char with `-` (so `/`, and on Windows `\` and `:`, all
510
+ * collapse). We only read this file cosmetically (to show "resume 5 KB" in
511
+ * the Invoking marker); actual capture is via stream-json. Returns null for
534
512
  * a sessionId that fails the safety check. */
535
513
  function transcriptPathFor(cwd, sessionId) {
536
514
  if (!isSafeSessionId(sessionId))
537
515
  return null;
538
- const slug = cwd.replace(/\//g, '-');
516
+ const slug = cwd.replace(/[^a-zA-Z0-9]/g, '-');
539
517
  return join(homedir(), '.claude', 'projects', slug, `${sessionId}.jsonl`);
540
518
  }
541
519
  /** `18.4s` when under a minute, `3:12.2` above. */
@@ -867,7 +845,7 @@ async function spawnSync(cwd, timeoutMs) {
867
845
  // verbatim (it may be a full path); only the default name is resolved.
868
846
  const cmd = process.env.GIPITY_RELAY_CLAUDE_CMD || resolveCommand('gipity');
869
847
  return new Promise((resolve, reject) => {
870
- const child = spawn(cmd, ['sync', '--json'], {
848
+ const child = spawnCommand(cmd, ['sync', '--json'], {
871
849
  cwd,
872
850
  env: process.env,
873
851
  stdio: ['ignore', 'pipe', 'pipe'],
@@ -921,7 +899,7 @@ export async function spawnGipityClaude(args, cwd, d) {
921
899
  const fullArgs = [...args, '--output-format', 'stream-json', '--verbose'];
922
900
  const env = { ...process.env, GIPITY_CONVERSATION_GUID: d.conversation_guid };
923
901
  return new Promise((resolve, reject) => {
924
- const child = spawn(cmd, fullArgs, { cwd, env, stdio: ['ignore', 'pipe', 'pipe'] });
902
+ const child = spawnCommand(cmd, fullArgs, { cwd, env, stdio: ['ignore', 'pipe', 'pipe'] });
925
903
  // `exited` fires when the child fully unwinds (exit event). Callers
926
904
  // like `killRunningForConv` await this before spawning a replacement
927
905
  // so the outgoing child has a chance to post its cancelled marker
@@ -126,6 +126,14 @@ WantedBy=default.target
126
126
  function windowsTaskPlan(cliPath) {
127
127
  const taskName = 'GipityRelay';
128
128
  const path = join(homedir(), 'AppData', 'Local', 'Gipity', 'relay-task.xml');
129
+ // Launch through node.exe explicitly. Task Scheduler's <Command> runs the
130
+ // target via CreateProcess, and a `.js` cliPath (what resolveCliPath yields)
131
+ // would resolve to its file association - Windows Script Host - which chokes
132
+ // on the shebang with "Invalid character" (800A03F6). Passing the entry as a
133
+ // node argument runs it under Node like every other platform.
134
+ const runsViaNode = /\.[cm]?js$/i.test(cliPath);
135
+ const command = runsViaNode ? process.execPath : cliPath;
136
+ const argLine = runsViaNode ? `"${cliPath}" relay run` : 'relay run';
129
137
  const content = `<?xml version="1.0" encoding="UTF-16"?>
130
138
  <Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
131
139
  <RegistrationInfo>
@@ -151,8 +159,8 @@ function windowsTaskPlan(cliPath) {
151
159
  </Settings>
152
160
  <Actions>
153
161
  <Exec>
154
- <Command>${cliPath}</Command>
155
- <Arguments>relay run</Arguments>
162
+ <Command>${command}</Command>
163
+ <Arguments>${argLine}</Arguments>
156
164
  </Exec>
157
165
  </Actions>
158
166
  </Task>
package/dist/setup.js CHANGED
@@ -5,7 +5,7 @@ import { resolve, join, dirname } from 'path';
5
5
  import { homedir } from 'os';
6
6
  import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
7
7
  import { spawnSync } from 'child_process';
8
- import { resolveCommand } from './platform.js';
8
+ import { resolveCommand, spawnSyncCommand } from './platform.js';
9
9
  import { SKILLS_CONTENT, BUILD_VS_NON_BUILD_RULE, DEFINITION_OF_DONE } from './knowledge.js';
10
10
  export { SKILLS_CONTENT };
11
11
  /** Canonical list of workstation artifacts that are NOT part of the project.
@@ -137,7 +137,7 @@ export const GIPITY_MARKETPLACE_REPO = 'GipityAI/claude-plugin';
137
137
  // an installed plugin when the marketplace advances - only an explicit
138
138
  // `plugin install`/`update` does - so this constant is how a CLI upgrade tells
139
139
  // ensureGipityPluginInstalled() to refresh a stale user-scope install.
140
- export const GIPITY_PLUGIN_VERSION = '0.4.0';
140
+ export const GIPITY_PLUGIN_VERSION = '0.4.1';
141
141
  /** True for hook commands the CLI itself wrote into settings.json in past
142
142
  * versions. Matched by signature so migration strips exactly our own
143
143
  * entries and never touches user-authored hooks. */
@@ -288,11 +288,11 @@ export function ensureGipityPluginInstalled() {
288
288
  // without an explicit path, so resolve it (otherwise the install silently
289
289
  // ENOENTs and the plugin's hooks never land at user scope).
290
290
  const claudeCmd = resolveCommand('claude');
291
- spawnSync(claudeCmd, ['plugin', 'marketplace', 'update', GIPITY_MARKETPLACE_NAME], {
291
+ spawnSyncCommand(claudeCmd, ['plugin', 'marketplace', 'update', GIPITY_MARKETPLACE_NAME], {
292
292
  stdio: 'ignore',
293
293
  timeout: 120_000,
294
294
  });
295
- spawnSync(claudeCmd, ['plugin', 'install', GIPITY_PLUGIN_ID, '--scope', 'user'], {
295
+ spawnSyncCommand(claudeCmd, ['plugin', 'install', GIPITY_PLUGIN_ID, '--scope', 'user'], {
296
296
  stdio: 'ignore',
297
297
  timeout: 120_000,
298
298
  });
@@ -1,8 +1,7 @@
1
1
  import { existsSync, mkdirSync, writeFileSync } from 'fs';
2
- import { spawnSync } from 'child_process';
3
2
  import { join } from 'path';
4
3
  import { LOCAL_DIR, LOCAL_ENTRY, LOCAL_PKG_DIR, writeState, readState } from './state.js';
5
- import { resolveCommand } from '../platform.js';
4
+ import { resolveCommand, spawnSyncCommand } from '../platform.js';
6
5
  export function isBootstrapped() {
7
6
  return existsSync(LOCAL_ENTRY);
8
7
  }
@@ -26,7 +25,7 @@ export function bootstrap(version, quiet = false) {
26
25
  // --ignore-scripts: don't run install lifecycle hooks (gipity ships
27
26
  // precompiled, deps need no build), so a compromised package can't execute
28
27
  // code during this self-managed install.
29
- const res = spawnSync(resolveCommand('npm'), ['install', '--no-audit', '--no-fund', '--ignore-scripts', `gipity@${version}`], {
28
+ const res = spawnSyncCommand(resolveCommand('npm'), ['install', '--no-audit', '--no-fund', '--ignore-scripts', `gipity@${version}`], {
30
29
  cwd: LOCAL_DIR,
31
30
  stdio: ['ignore', 'pipe', 'pipe'],
32
31
  encoding: 'utf-8',
@@ -1,10 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  // Background updater. Invoked detached by the shim; can also be invoked
3
3
  // directly by `gipity update --force`.
4
- import { spawnSync } from 'child_process';
5
4
  import { appendFileSync, existsSync } from 'fs';
6
5
  import { LOCAL_DIR, LOCAL_ENTRY, UPDATE_LOG, readState, writeState, updatesDisabled } from './state.js';
7
- import { resolveCommand } from '../platform.js';
6
+ import { resolveCommand, spawnSyncCommand } from '../platform.js';
8
7
  const CHECK_INTERVAL_MS = 4 * 60 * 60 * 1000; // 4 hours
9
8
  function log(line) {
10
9
  try {
@@ -38,7 +37,7 @@ function installVersion(version) {
38
37
  // --ignore-scripts: this runs unattended in the background, so don't let a
39
38
  // compromised package's install lifecycle hooks execute. gipity ships
40
39
  // precompiled (dist/) and its deps need no build step, so nothing is lost.
41
- const res = spawnSync(resolveCommand('npm'), ['install', '--silent', '--no-audit', '--no-fund', '--ignore-scripts', `gipity@${version}`], {
40
+ const res = spawnSyncCommand(resolveCommand('npm'), ['install', '--silent', '--no-audit', '--no-fund', '--ignore-scripts', `gipity@${version}`], {
42
41
  cwd: LOCAL_DIR,
43
42
  stdio: 'ignore',
44
43
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gipity",
3
- "version": "1.0.406",
3
+ "version": "1.0.408",
4
4
  "description": "The full-stack platform tuned for AI agents. Database, storage, auth, functions, deploy, and drop-in kits - all agent-tuned. Pair with Claude Code or use standalone.",
5
5
  "bin": {
6
6
  "gipity": "dist/updater/shim.js",
@@ -13,7 +13,7 @@
13
13
  "postbuild": "node scripts/gen-build-info.mjs",
14
14
  "dev": "tsc --watch",
15
15
  "test": "npm run test:smoke",
16
- "test:smoke": "tsc && node --test dist/__tests__/utils.test.js dist/__tests__/colors.test.js dist/__tests__/config.test.js dist/__tests__/sync.test.js dist/__tests__/sync-apply.test.js dist/__tests__/sync-lock.test.js dist/__tests__/auth-lock.test.js dist/__tests__/api-401-retry.test.js dist/__tests__/push-cas.test.js dist/__tests__/upload.test.js dist/__tests__/progress.test.js dist/__tests__/updater.test.js dist/__tests__/cli-smoke.test.js dist/__tests__/claude-noninteractive.test.js dist/__tests__/claude-trust.test.js dist/__tests__/relay-state.test.js dist/__tests__/relay-daemon.test.js dist/__tests__/relay-installers.test.js dist/__tests__/relay-bridge-abort.test.js dist/__tests__/relay-redact.test.js dist/__tests__/relay-machine-id.test.js dist/__tests__/stream-json.test.js dist/__tests__/relay-ingest-contract.test.js dist/__tests__/prompts.test.js dist/__tests__/capture-transcript.test.js dist/__tests__/flag-aliases.test.js dist/__tests__/client-context.test.js dist/__tests__/adopt-cwd.test.js dist/__tests__/cli-cmd-agent.test.js dist/__tests__/cli-cmd-approval.test.js dist/__tests__/cli-cmd-audit.test.js dist/__tests__/cli-cmd-chat.test.js dist/__tests__/cli-cmd-credits.test.js dist/__tests__/cli-cmd-db.test.js dist/__tests__/cli-cmd-deploy.test.js dist/__tests__/cli-cmd-domain.test.js dist/__tests__/cli-cmd-email.test.js dist/__tests__/cli-cmd-file.test.js dist/__tests__/cli-cmd-fn.test.js dist/__tests__/cli-cmd-service.test.js dist/__tests__/cli-cmd-job.test.js dist/__tests__/cli-cmd-generate.test.js dist/__tests__/cli-cmd-gmail.test.js dist/__tests__/cli-cmd-info.test.js dist/__tests__/cli-cmd-init.test.js dist/__tests__/cli-cmd-location.test.js dist/__tests__/cli-cmd-text.test.js dist/__tests__/cli-cmd-login.test.js dist/__tests__/cli-cmd-logout.test.js dist/__tests__/cli-cmd-token.test.js dist/__tests__/cli-cmd-logs.test.js dist/__tests__/cli-cmd-memory.test.js dist/__tests__/cli-cmd-page.test.js dist/__tests__/cli-cmd-plan.test.js dist/__tests__/cli-cmd-project.test.js dist/__tests__/cli-cmd-rbac.test.js dist/__tests__/cli-cmd-realtime.test.js dist/__tests__/cli-cmd-records.test.js dist/__tests__/cli-cmd-relay.test.js dist/__tests__/cli-cmd-doctor.test.js dist/__tests__/claude-setup.test.js dist/__tests__/cli-cmd-sandbox.test.js dist/__tests__/cli-cmd-add.test.js dist/__tests__/cli-cmd-remove.test.js dist/__tests__/cli-cmd-skill.test.js dist/__tests__/cli-cmd-test.test.js dist/__tests__/cli-cmd-workflow.test.js dist/__tests__/setup-skills-block.test.js dist/__tests__/setup-hooks.test.js",
16
+ "test:smoke": "tsc && node --test dist/__tests__/utils.test.js dist/__tests__/platform.test.js dist/__tests__/colors.test.js dist/__tests__/config.test.js dist/__tests__/sync.test.js dist/__tests__/sync-apply.test.js dist/__tests__/sync-lock.test.js dist/__tests__/auth-lock.test.js dist/__tests__/api-401-retry.test.js dist/__tests__/push-cas.test.js dist/__tests__/upload.test.js dist/__tests__/progress.test.js dist/__tests__/updater.test.js dist/__tests__/cli-smoke.test.js dist/__tests__/claude-noninteractive.test.js dist/__tests__/claude-trust.test.js dist/__tests__/relay-state.test.js dist/__tests__/relay-daemon.test.js dist/__tests__/relay-installers.test.js dist/__tests__/relay-bridge-abort.test.js dist/__tests__/relay-redact.test.js dist/__tests__/relay-machine-id.test.js dist/__tests__/stream-json.test.js dist/__tests__/relay-ingest-contract.test.js dist/__tests__/prompts.test.js dist/__tests__/capture-transcript.test.js dist/__tests__/flag-aliases.test.js dist/__tests__/client-context.test.js dist/__tests__/adopt-cwd.test.js dist/__tests__/cli-cmd-agent.test.js dist/__tests__/cli-cmd-approval.test.js dist/__tests__/cli-cmd-audit.test.js dist/__tests__/cli-cmd-chat.test.js dist/__tests__/cli-cmd-credits.test.js dist/__tests__/cli-cmd-db.test.js dist/__tests__/cli-cmd-deploy.test.js dist/__tests__/cli-cmd-domain.test.js dist/__tests__/cli-cmd-email.test.js dist/__tests__/cli-cmd-file.test.js dist/__tests__/cli-cmd-fn.test.js dist/__tests__/cli-cmd-service.test.js dist/__tests__/cli-cmd-job.test.js dist/__tests__/cli-cmd-generate.test.js dist/__tests__/cli-cmd-gmail.test.js dist/__tests__/cli-cmd-info.test.js dist/__tests__/cli-cmd-init.test.js dist/__tests__/cli-cmd-location.test.js dist/__tests__/cli-cmd-text.test.js dist/__tests__/cli-cmd-login.test.js dist/__tests__/cli-cmd-logout.test.js dist/__tests__/cli-cmd-token.test.js dist/__tests__/cli-cmd-logs.test.js dist/__tests__/cli-cmd-memory.test.js dist/__tests__/cli-cmd-page.test.js dist/__tests__/cli-cmd-plan.test.js dist/__tests__/cli-cmd-project.test.js dist/__tests__/cli-cmd-rbac.test.js dist/__tests__/cli-cmd-realtime.test.js dist/__tests__/cli-cmd-records.test.js dist/__tests__/cli-cmd-relay.test.js dist/__tests__/cli-cmd-doctor.test.js dist/__tests__/claude-setup.test.js dist/__tests__/cli-cmd-sandbox.test.js dist/__tests__/cli-cmd-add.test.js dist/__tests__/cli-cmd-remove.test.js dist/__tests__/cli-cmd-skill.test.js dist/__tests__/cli-cmd-test.test.js dist/__tests__/cli-cmd-workflow.test.js dist/__tests__/setup-skills-block.test.js dist/__tests__/setup-hooks.test.js",
17
17
  "test:e2e": "tsc && GIPITY_E2E=1 node --test --test-timeout=180000 dist/__tests__/cli-e2e-live.test.js dist/__tests__/cli-e2e-sync-live.test.js dist/__tests__/cli-e2e-services-media-live.test.js dist/__tests__/cli-e2e-workflow-live.test.js dist/__tests__/cli-e2e-sandbox-live.test.js dist/__tests__/cli-e2e-page-fetch-live.test.js dist/__tests__/cli-e2e-page-test-live.test.js",
18
18
  "test:e2e:sync": "tsc && GIPITY_E2E=1 node --test --test-timeout=180000 dist/__tests__/cli-e2e-sync-live.test.js",
19
19
  "test:e2e:sandbox": "tsc && GIPITY_E2E=1 node --test --test-timeout=180000 dist/__tests__/cli-e2e-sandbox-live.test.js"
@@ -33,8 +33,7 @@
33
33
  },
34
34
  "files": [
35
35
  "dist/**/*.js",
36
- "!dist/__tests__/**",
37
- "hooks"
36
+ "!dist/__tests__/**"
38
37
  ],
39
38
  "license": "MIT"
40
39
  }
@@ -1,17 +0,0 @@
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
package/hooks/pre-turn.sh DELETED
@@ -1,20 +0,0 @@
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