gipity 1.0.306 → 1.0.318

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 (153) hide show
  1. package/README.md +16 -13
  2. package/dist/__tests__/claude-noninteractive.test.js +1 -1
  3. package/dist/__tests__/cli-e2e-live.test.js +3 -3
  4. package/dist/__tests__/helpers/spawn-cli.d.ts +1 -1
  5. package/dist/__tests__/helpers/spawn-cli.js +2 -2
  6. package/dist/__tests__/prompts.test.js +3 -3
  7. package/dist/__tests__/push-cas.test.js +2 -2
  8. package/dist/__tests__/relay-bridge-abort.test.js +2 -2
  9. package/dist/__tests__/relay-daemon.test.js +5 -5
  10. package/dist/__tests__/relay-ingest-contract.test.js +2 -2
  11. package/dist/__tests__/relay-installers.test.js +4 -4
  12. package/dist/__tests__/relay-state.test.js +1 -1
  13. package/dist/__tests__/stream-json.test.js +2 -2
  14. package/dist/__tests__/sync-apply.test.js +7 -7
  15. package/dist/__tests__/sync-lock.test.js +4 -4
  16. package/dist/__tests__/sync.test.js +2 -2
  17. package/dist/__tests__/updater.test.js +1 -1
  18. package/dist/adopt-cwd.d.ts +2 -2
  19. package/dist/adopt-cwd.js +9 -9
  20. package/dist/api.d.ts +1 -1
  21. package/dist/api.js +1 -1
  22. package/dist/auth.js +1 -1
  23. package/dist/banner.js +29 -29
  24. package/dist/banner.js.map +1 -1
  25. package/dist/capture/sources/claude-code.d.ts +2 -2
  26. package/dist/capture/sources/claude-code.js +4 -4
  27. package/dist/colors.js +1 -1
  28. package/dist/commands/agent.js +18 -22
  29. package/dist/commands/agent.js.map +1 -1
  30. package/dist/commands/api.js +1 -1
  31. package/dist/commands/approval.d.ts +2 -0
  32. package/dist/commands/approval.js +117 -0
  33. package/dist/commands/approval.js.map +1 -0
  34. package/dist/commands/chat.js +40 -2
  35. package/dist/commands/chat.js.map +1 -1
  36. package/dist/commands/claude.js +19 -19
  37. package/dist/commands/claude.js.map +1 -1
  38. package/dist/commands/credits.js +2 -2
  39. package/dist/commands/credits.js.map +1 -1
  40. package/dist/commands/db.js +9 -6
  41. package/dist/commands/db.js.map +1 -1
  42. package/dist/commands/deploy.js +2 -2
  43. package/dist/commands/deploy.js.map +1 -1
  44. package/dist/commands/doctor.js +2 -2
  45. package/dist/commands/doctor.js.map +1 -1
  46. package/dist/commands/domain.js +2 -2
  47. package/dist/commands/email.js +30 -5
  48. package/dist/commands/email.js.map +1 -1
  49. package/dist/commands/file.js +11 -21
  50. package/dist/commands/file.js.map +1 -1
  51. package/dist/commands/fn.js +2 -2
  52. package/dist/commands/fn.js.map +1 -1
  53. package/dist/commands/generate.js +2 -2
  54. package/dist/commands/generate.js.map +1 -1
  55. package/dist/commands/gmail.d.ts +2 -0
  56. package/dist/commands/gmail.js +85 -0
  57. package/dist/commands/gmail.js.map +1 -0
  58. package/dist/commands/init.js +2 -2
  59. package/dist/commands/init.js.map +1 -1
  60. package/dist/commands/location.js +1 -1
  61. package/dist/commands/location.js.map +1 -1
  62. package/dist/commands/login.js +27 -13
  63. package/dist/commands/login.js.map +1 -1
  64. package/dist/commands/logout.js +7 -4
  65. package/dist/commands/logout.js.map +1 -1
  66. package/dist/commands/logs.js +2 -2
  67. package/dist/commands/logs.js.map +1 -1
  68. package/dist/commands/memory.js +8 -18
  69. package/dist/commands/memory.js.map +1 -1
  70. package/dist/commands/page-inspect.js +2 -2
  71. package/dist/commands/page-inspect.js.map +1 -1
  72. package/dist/commands/page-screenshot.js +3 -3
  73. package/dist/commands/page-screenshot.js.map +1 -1
  74. package/dist/commands/project.js +11 -26
  75. package/dist/commands/project.js.map +1 -1
  76. package/dist/commands/push.js +6 -2
  77. package/dist/commands/push.js.map +1 -1
  78. package/dist/commands/rbac.js +7 -7
  79. package/dist/commands/rbac.js.map +1 -1
  80. package/dist/commands/records.js +10 -8
  81. package/dist/commands/records.js.map +1 -1
  82. package/dist/commands/relay-install.d.ts +2 -2
  83. package/dist/commands/relay-install.js +2 -2
  84. package/dist/commands/relay-install.js.map +1 -1
  85. package/dist/commands/relay.d.ts +1 -1
  86. package/dist/commands/relay.js +15 -15
  87. package/dist/commands/relay.js.map +1 -1
  88. package/dist/commands/sandbox.js +3 -3
  89. package/dist/commands/sandbox.js.map +1 -1
  90. package/dist/commands/scaffold.js +3 -3
  91. package/dist/commands/scaffold.js.map +1 -1
  92. package/dist/commands/skill.d.ts +2 -0
  93. package/dist/commands/skill.js +45 -0
  94. package/dist/commands/skill.js.map +1 -0
  95. package/dist/commands/skills.js +3 -3
  96. package/dist/commands/skills.js.map +1 -1
  97. package/dist/commands/status.js +2 -2
  98. package/dist/commands/status.js.map +1 -1
  99. package/dist/commands/sync.js +1 -1
  100. package/dist/commands/sync.js.map +1 -1
  101. package/dist/commands/test.js +5 -5
  102. package/dist/commands/test.js.map +1 -1
  103. package/dist/commands/uninstall.d.ts +2 -2
  104. package/dist/commands/uninstall.js +7 -7
  105. package/dist/commands/uninstall.js.map +1 -1
  106. package/dist/commands/update.js +4 -1
  107. package/dist/commands/update.js.map +1 -1
  108. package/dist/commands/upload.js +2 -2
  109. package/dist/commands/upload.js.map +1 -1
  110. package/dist/commands/workflow.js +51 -22
  111. package/dist/commands/workflow.js.map +1 -1
  112. package/dist/config.js +3 -3
  113. package/dist/help-skills.js +1 -1
  114. package/dist/helpers/command.d.ts +1 -1
  115. package/dist/helpers/command.js +1 -1
  116. package/dist/helpers/index.d.ts +2 -2
  117. package/dist/helpers/index.js +2 -2
  118. package/dist/helpers/index.js.map +1 -1
  119. package/dist/helpers/output.d.ts +5 -1
  120. package/dist/helpers/output.js +15 -2
  121. package/dist/helpers/output.js.map +1 -1
  122. package/dist/helpers/sync.d.ts +1 -1
  123. package/dist/helpers/sync.js +1 -1
  124. package/dist/hooks/capture-runner.d.ts +2 -2
  125. package/dist/hooks/capture-runner.js +7 -7
  126. package/dist/index.js +11 -9
  127. package/dist/index.js.map +1 -1
  128. package/dist/project-setup.js +2 -2
  129. package/dist/prompts.d.ts +12 -12
  130. package/dist/prompts.js +44 -44
  131. package/dist/provider-docs.d.ts +4 -4
  132. package/dist/provider-docs.js +6 -6
  133. package/dist/provider-docs.js.map +1 -1
  134. package/dist/relay/daemon.d.ts +1 -1
  135. package/dist/relay/daemon.js +75 -37
  136. package/dist/relay/daemon.js.map +1 -1
  137. package/dist/relay/installers.d.ts +1 -1
  138. package/dist/relay/installers.js +11 -11
  139. package/dist/relay/onboarding.js +6 -6
  140. package/dist/relay/state.d.ts +1 -1
  141. package/dist/relay/state.js +6 -6
  142. package/dist/relay/stream-json.d.ts +3 -3
  143. package/dist/relay/stream-json.js +4 -4
  144. package/dist/setup.d.ts +5 -5
  145. package/dist/setup.js +10 -10
  146. package/dist/sync.js +11 -11
  147. package/dist/updater/bootstrap.d.ts +1 -1
  148. package/dist/updater/bootstrap.js +2 -2
  149. package/dist/updater/shim.js +1 -1
  150. package/dist/upload.js +3 -3
  151. package/dist/utils.d.ts +3 -3
  152. package/dist/utils.js +4 -4
  153. package/package.json +2 -2
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Gipity CLI
2
2
 
3
- Most AI tools give you a chatbot. We gave ours a computer.
3
+ The agent-tuned platform where AI-built apps live.
4
4
 
5
- [Gipity](https://gipity.ai) is an AI agent with 90+ tools and a full cloud platform — app hosting, databases, file storage, deployment, workflows, code execution, and more. Use it standalone or plug it into Claude Code to give your local agent cloud superpowers.
5
+ [Gipity](https://gipity.ai) is the platform: hosting, databases, file storage, deployment, workflows, code execution, and monitoring. Agent-tuned from scaffold to deploy. Use standalone, or pair with Claude Code to give your local agent cloud superpowers. Any model, any infra, always your code.
6
6
 
7
- This CLI connects [Claude Code](https://claude.ai/claude-code) to Gipity's cloud platform databases, deployment, browser testing, image gen, and 50+ other capabilities your local agent doesn't have. It also syncs files so Claude Code and the Gipity web agent share the same project.
7
+ This CLI connects [Claude Code](https://claude.ai/claude-code) to Gipity's cloud platform - databases, deployment, browser testing, image gen, and 50+ other capabilities your local agent doesn't have. It also syncs files so Claude Code and the Gipity web agent share the same project.
8
8
 
9
9
  ## Getting Started
10
10
 
@@ -16,7 +16,7 @@ You need **Node.js 18+** (which includes npm) and **Claude Code**.
16
16
  # macOS
17
17
  brew install node
18
18
 
19
- # Windows download the installer from https://nodejs.org
19
+ # Windows - download the installer from https://nodejs.org
20
20
 
21
21
  # Linux (Ubuntu/Debian)
22
22
  curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt install -y nodejs
@@ -32,7 +32,7 @@ That's it. `claude` walks you through login, project setup, and launches Claude
32
32
 
33
33
  ## Updates
34
34
 
35
- The CLI auto-updates in the background. After your one-time `npm install -g gipity`, every run silently checks npm for a new version and installs it into `~/.gipity/local/` no sudo, no re-running install commands. The new version takes effect on your next invocation.
35
+ The CLI auto-updates in the background. After your one-time `npm install -g gipity`, every run silently checks npm for a new version and installs it into `~/.gipity/local/` - no sudo, no re-running install commands. The new version takes effect on your next invocation.
36
36
 
37
37
  ```bash
38
38
  gipity doctor # show install version, last update check, opt-out status
@@ -75,7 +75,7 @@ That's it. You'll see:
75
75
 
76
76
  If you're already logged in, it skips straight to project setup. If you already have a project in the current directory, it skips straight to launching Claude Code.
77
77
 
78
- Projects live in `~/GipityProjects/{project-slug}/` created automatically on first use. Any extra flags (like `--dangerously-skip-permissions`, `--model opus`, etc.) get passed through to Claude.
78
+ Projects live in `~/GipityProjects/{project-slug}/` - created automatically on first use. Any extra flags (like `--dangerously-skip-permissions`, `--model opus`, etc.) get passed through to Claude.
79
79
 
80
80
  ### The manual way
81
81
 
@@ -93,11 +93,11 @@ claude
93
93
 
94
94
  This is the good part. When you run `gipity init` in a project, it sets up two hooks in `.claude/settings.json`:
95
95
 
96
- **Auto-push** Every time Claude Code writes or edits a file, it gets pushed to Gipity in the background. No extra steps.
96
+ **Auto-push** - Every time Claude Code writes or edits a file, it gets pushed to Gipity in the background. No extra steps.
97
97
 
98
- **Auto-pull** Before each turn, Claude Code pulls any changes that happened remotely (like if your Gipity agent built something via chat). Claude sees what changed and can pick up where things left off.
98
+ **Auto-pull** - Before each turn, Claude Code pulls any changes that happened remotely (like if your Gipity agent built something via chat). Claude sees what changed and can pick up where things left off.
99
99
 
100
- That means Claude Code and your Gipity agent share the same files, same project, same context. You get the best of both Claude Code for hands-on coding, Gipity for autonomous agent work.
100
+ That means Claude Code and your Gipity agent share the same files, same project, same context. You get the best of both - Claude Code for hands-on coding, Gipity for autonomous agent work.
101
101
 
102
102
  ### What gets set up
103
103
 
@@ -122,7 +122,7 @@ gipity sync down # Pull remote changes
122
122
 
123
123
  | Command | What it does |
124
124
  |---------|-------------|
125
- | `gipity claude` | Log in, pick a project, and launch Claude Code all in one |
125
+ | `gipity claude` | Log in, pick a project, and launch Claude Code - all in one |
126
126
  | `gipity login` | Authenticate with email + verification code |
127
127
  | `gipity init` | Set up a Gipity project and configure Claude Code |
128
128
  | `gipity status` | Show project, agent, and auth info |
@@ -135,6 +135,7 @@ gipity sync down # Pull remote changes
135
135
  | `gipity sandbox run <code>` | Execute code in a sandboxed container |
136
136
  | `gipity project` | List, create, switch, or delete projects |
137
137
  | `gipity agent` | List, create, switch, or configure agents |
138
+ | `gipity approval` | List, create, answer, or cancel pending approvals |
138
139
  | `gipity workflow` | Manage and trigger automated workflows |
139
140
  | `gipity file` | Browse remote files (ls, cat, tree) |
140
141
  | `gipity scaffold [title]` | Create app structure (`--type web`, `--type 2d-game`, or `--type 3d-world`) |
@@ -146,9 +147,11 @@ gipity sync down # Pull remote changes
146
147
  | `gipity rbac` | Manage RBAC policies |
147
148
  | `gipity audit` | Query audit logs |
148
149
  | `gipity credits` | Check your balance and usage |
149
- | `gipity skills` | List and manage agent skills |
150
+ | `gipity skill` | List and manage agent skills |
151
+ | `gipity chat [list\|rename\|archive\|delete]` | Manage chats (or `gipity chat <message>` to send) |
152
+ | `gipity gmail [send\|reply\|search\|read]` | Send/read via your own Gmail (different from `gipity email`) |
150
153
  | `gipity domain` | Manage custom domains for deployed apps |
151
- | `gipity email` | Send emails via your agent |
154
+ | `gipity email [send]` | Send emails from the platform (gipity@gipity.ai) |
152
155
  | `gipity generate` | Generate images, audio, or video via your agent |
153
156
  | `gipity logout` | Sign out and clear local tokens |
154
157
 
@@ -253,7 +256,7 @@ Your login tokens. Created by `gipity login`. Tokens auto-refresh so you shouldn
253
256
 
254
257
  ## Questions?
255
258
 
256
- Reach out anytime steve@gipity.ai
259
+ Reach out anytime - steve@gipity.ai
257
260
 
258
261
  This is early and moving fast. If something's broken or confusing, I want to hear about it.
259
262
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `gipity claude -p "msg"` / `--print` non-interactive passthrough mode.
2
+ * `gipity claude -p "msg"` / `--print` - non-interactive passthrough mode.
3
3
  *
4
4
  * Exercises the early-exit preconditions (must be logged in + must have a
5
5
  * project in cwd) since the success path shells out to `claude` which isn't
@@ -20,7 +20,7 @@ const E2E_ENABLED = process.env['GIPITY_E2E'] === '1';
20
20
  const API_BASE = process.env['GIPITY_E2E_API_BASE'] ?? 'https://a.gipity.ai';
21
21
  const EMAIL = process.env['GIPITY_E2E_EMAIL'] ?? 'ec-cli-e2e@914-6.com';
22
22
  const CODE = process.env['GIPITY_E2E_CODE'] ?? '914914';
23
- // Email convention guard protect against accidentally invoking real SendGrid.
23
+ // Email convention guard - protect against accidentally invoking real SendGrid.
24
24
  if (E2E_ENABLED && !EMAIL.startsWith('ec')) {
25
25
  throw new Error(`E2E test email must start with "ec" to suppress real outbound mail: got "${EMAIL}"`);
26
26
  }
@@ -88,7 +88,7 @@ describe('cli-e2e-live', { skip: !E2E_ENABLED && 'set GIPITY_E2E=1 to run' }, ()
88
88
  it('4b. deploy dev again is idempotent (no changes)', () => {
89
89
  const r = cli(['deploy', 'dev'], { timeout: 60000 });
90
90
  assert.equal(r.status, 0);
91
- // No strict assertion on output text phases may say "skipped" or "ok"
91
+ // No strict assertion on output text - phases may say "skipped" or "ok"
92
92
  // depending on whether checksums caught everything. Just confirm exit 0.
93
93
  });
94
94
  it('4c. deploy dev --only functions filters phases', () => {
@@ -137,7 +137,7 @@ describe('cli-e2e-live', { skip: !E2E_ENABLED && 'set GIPITY_E2E=1 to run' }, ()
137
137
  it('9. doctor reports sane install info with auth', () => {
138
138
  const r = cli(['doctor']);
139
139
  assert.equal(r.status, 0);
140
- assert.match(r.stdout, /Gipity CLI doctor/);
140
+ assert.match(r.stdout, /Gipity CLI - doctor/);
141
141
  assert.match(r.stdout, /shim version/);
142
142
  });
143
143
  });
@@ -21,7 +21,7 @@ export interface SpawnOptions {
21
21
  export declare function runCli(args: string[], opts?: SpawnOptions): SpawnResult;
22
22
  export declare function makeTmpHome(): string;
23
23
  /**
24
- * Async version of runCli uses `spawn` instead of `spawnSync` so the
24
+ * Async version of runCli - uses `spawn` instead of `spawnSync` so the
25
25
  * test's event loop keeps turning while the child runs. Required for any
26
26
  * test that spins up an in-process HTTP server for the child to hit
27
27
  * (spawnSync deadlocks because the server can't accept connections while
@@ -4,7 +4,7 @@ import { dirname, resolve } from 'path';
4
4
  import { mkdtempSync } from 'fs';
5
5
  import { tmpdir } from 'os';
6
6
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
- // __dirname is dist/__tests__/helpers CLI entry is dist/index.js
7
+ // __dirname is dist/__tests__/helpers - CLI entry is dist/index.js
8
8
  export const CLI_ENTRY = resolve(__dirname, '..', '..', 'index.js');
9
9
  /**
10
10
  * Run the built gipity CLI with a deterministic, isolated environment.
@@ -37,7 +37,7 @@ export function makeTmpHome() {
37
37
  return mkdtempSync(`${tmpdir()}/gipity-cli-test-`);
38
38
  }
39
39
  /**
40
- * Async version of runCli uses `spawn` instead of `spawnSync` so the
40
+ * Async version of runCli - uses `spawn` instead of `spawnSync` so the
41
41
  * test's event loop keeps turning while the child runs. Required for any
42
42
  * test that spins up an in-process HTTP server for the child to hit
43
43
  * (spawnSync deadlocks because the server can't accept connections while
@@ -4,7 +4,7 @@
4
4
  * The user's actual message must sit between the `USER_MSG_OPEN` /
5
5
  * `USER_MSG_CLOSE` tags with no trailing instructions, and the client-side
6
6
  * `stripPreamble` in `platform/client/src/ts/commands/claude-display.ts`
7
- * must use identical tag strings otherwise the web CLI renders the full
7
+ * must use identical tag strings - otherwise the web CLI renders the full
8
8
  * wrap as a `claude>` turn (the historical bug: duplicate user turns
9
9
  * rendered as walls of preamble text).
10
10
  */
@@ -16,7 +16,7 @@ import { dirname, resolve } from 'path';
16
16
  import { USER_MSG_OPEN, USER_MSG_CLOSE, buildFreshWrap, buildResumeWrap, } from '../prompts.js';
17
17
  const HERE = dirname(fileURLToPath(import.meta.url));
18
18
  // dist/__tests__/ → repo root depends on build layout. These tests also run
19
- // against the source tree via tsx in CI handle both by walking up until we
19
+ // against the source tree via tsx in CI - handle both by walking up until we
20
20
  // find the platform/ sibling.
21
21
  function repoRoot() {
22
22
  let p = HERE;
@@ -109,7 +109,7 @@ function stripPreambleReplica(s) {
109
109
  }
110
110
  describe('stripPreamble round-trip', () => {
111
111
  it('recovers the exact user message from buildFreshWrap output', () => {
112
- const msg = 'hello world whats 2+2 and also a newline\nplease';
112
+ const msg = 'hello world - whats 2+2 and also a newline\nplease';
113
113
  const out = buildFreshWrap('## ctx\n- Name: foo\n- Files: empty', msg);
114
114
  assert.equal(stripPreambleReplica(out), msg);
115
115
  });
@@ -84,7 +84,7 @@ describe('pushFile CAS', () => {
84
84
  const { pushFile } = await import('../sync.js');
85
85
  await assert.rejects(() => pushFile(join(projectDir, 'hello.txt')), /newer version.*serverVersion=7.*gipity sync/is);
86
86
  });
87
- it('succeeds when baseline matches pushFile updates serverVersion in baseline', async () => {
87
+ it('succeeds when baseline matches - pushFile updates serverVersion in baseline', async () => {
88
88
  writeFileSync(join(projectDir, '.gipity', 'sync-state.json'), JSON.stringify({
89
89
  projectGuid: 'proj_test',
90
90
  files: {
@@ -111,7 +111,7 @@ describe('pushFile CAS', () => {
111
111
  }), { status: 200, headers: { 'content-type': 'application/json' } });
112
112
  }
113
113
  if (url.startsWith('https://s3.example')) {
114
- // Presigned PUT return an etag.
114
+ // Presigned PUT - return an etag.
115
115
  return new Response('', { status: 200, headers: { etag: '"fake-etag"' } });
116
116
  }
117
117
  if (url.includes('/files/upload-complete')) {
@@ -29,7 +29,7 @@ describe('bridgeAbort', () => {
29
29
  assert.equal(inner.signal.aborted, true);
30
30
  assert.equal(inner.signal.reason, 'pre');
31
31
  });
32
- it('detaches cleanly does not leak listeners across many calls', () => {
32
+ it('detaches cleanly - does not leak listeners across many calls', () => {
33
33
  const outer = new AbortController();
34
34
  // Sanity: listenerCount via public API isn't part of EventTarget, so we
35
35
  // verify no leak by running many cycles and confirming the process does
@@ -52,7 +52,7 @@ describe('bridgeAbort', () => {
52
52
  assert.equal(leakWarn, undefined, `leaked listeners: ${leakWarn}`);
53
53
  // After all detaches, firing outer.abort must not affect any prior
54
54
  // inners (they're out of scope / already detached). Create one more
55
- // inner WITHOUT detaching and confirm it receives the abort proves
55
+ // inner WITHOUT detaching and confirm it receives the abort - proves
56
56
  // the bridge still works after many attach/detach cycles.
57
57
  const liveInner = new AbortController();
58
58
  bridgeAbort(outer.signal, liveInner);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `gipity-relay` daemon full round-trip tests against an in-process mock
2
+ * `gipity-relay` daemon - full round-trip tests against an in-process mock
3
3
  * backend. `GIPITY_RELAY_CLAUDE_CMD` is pointed at `true` / `false` / a
4
4
  * sleep script so we can assert the daemon's ack shape for each outcome
5
5
  * without actually shelling out to `gipity claude`.
@@ -61,7 +61,7 @@ before(async () => {
61
61
  }
62
62
  const d = pending.shift();
63
63
  if (!d) {
64
- // Brief wait then 204 simulates a short long-poll hold.
64
+ // Brief wait then 204 - simulates a short long-poll hold.
65
65
  await new Promise(r => setTimeout(r, 80));
66
66
  res.statusCode = 204;
67
67
  return res.end();
@@ -86,7 +86,7 @@ before(async () => {
86
86
  after(async () => {
87
87
  await new Promise(resolve => server.close(() => resolve()));
88
88
  });
89
- // ─── Fixture a fresh $HOME with a paired device + pre-seeded project dir ─
89
+ // ─── Fixture - a fresh $HOME with a paired device + pre-seeded project dir ─
90
90
  function freshHome(opts = {}) {
91
91
  const home = mkdtempSync(join(tmpdir(), 'gipity-daemon-'));
92
92
  const projectsRoot = join(home, 'GipityProjects');
@@ -205,11 +205,11 @@ describe('daemon: safety checks', () => {
205
205
  describe('daemon: auto-bootstrap missing project dir', () => {
206
206
  it('creates ~/GipityProjects/<slug>/ + .gipity.json when dispatch targets an unknown project', async () => {
207
207
  resetMock();
208
- // Don't preseed the project dir daemon should create it.
208
+ // Don't preseed the project dir - daemon should create it.
209
209
  const { home, projectCwd } = freshHome({ preseedProject: false });
210
210
  pending.push(dispatchRow({ short_guid: 'rds_bootstrap', message: 'hi' }));
211
211
  await runDaemon(home, 'true');
212
- // Ack should be "done" the dispatch ran successfully against the new dir.
212
+ // Ack should be "done" - the dispatch ran successfully against the new dir.
213
213
  assert.equal(acks.length, 1);
214
214
  assert.equal(acks[0].status, 'done', `got ${acks[0].status}: ${acks[0].error}`);
215
215
  // Directory + .gipity.json now exist with the right guid.
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Contract test: every key the daemon emits in an ingest entry must appear
3
- * in the manifest below which mirrors the server's `entrySchema`
3
+ * in the manifest below - which mirrors the server's `entrySchema`
4
4
  * (platform/server/src/routes/remote-sessions.ts). If `mapEventToEntries`
5
5
  * ever stamps a key the server doesn't accept, this test fails loudly
6
6
  * BEFORE the daemon ships and starts 400ing in production.
7
7
  *
8
8
  * Why a manifest instead of importing the server's Zod schema directly:
9
9
  * the CLI is a standalone npm package with a strict `rootDir: src`
10
- * tsconfig a cross-workspace import won't typecheck. The manifest is
10
+ * tsconfig - a cross-workspace import won't typecheck. The manifest is
11
11
  * the smallest thing that catches the class of bug we hit (the daemon
12
12
  * adding a `ts` field the server stripped silently, then later rejected).
13
13
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Platform-specific service-unit generation pure file-content checks.
2
+ * Platform-specific service-unit generation - pure file-content checks.
3
3
  * Actually running launchctl/systemctl/schtasks is user-driven; not tested.
4
4
  */
5
5
  import { describe, it } from 'node:test';
@@ -74,12 +74,12 @@ describe('installers: enable/disable commands look right', () => {
74
74
  assert.ok(p.enableCmds[1].includes('/Run'));
75
75
  assert.ok(p.disableCmds.some(argv => argv.includes('/Delete')));
76
76
  });
77
- it('argv arrays are flat string arrays no shell metacharacters injected', () => {
77
+ it('argv arrays are flat string arrays - no shell metacharacters injected', () => {
78
78
  // A path with spaces must stay as a single argv slot, not split by sh.
79
79
  const cliPath = '/Users/Test User/.npm-global/bin/gipity';
80
80
  const p = planFor({ cliPath, platformOverride: 'darwin' });
81
81
  // The plist path also contains the homedir, which on the test runner
82
- // doesn't contain spaces but the contract is still: argv elements
82
+ // doesn't contain spaces - but the contract is still: argv elements
83
83
  // are single strings, never shell-tokenized.
84
84
  for (const argv of [...p.enableCmds, ...p.disableCmds, p.statusCmd]) {
85
85
  for (const part of argv) {
@@ -87,7 +87,7 @@ describe('installers: enable/disable commands look right', () => {
87
87
  assert.ok(!part.includes('\n'), 'argv parts should not contain newlines');
88
88
  }
89
89
  }
90
- // CLI path is embedded in the plist content (file body), not the argv
90
+ // CLI path is embedded in the plist content (file body), not the argv -
91
91
  // sanity check that didn't change.
92
92
  assert.match(p.content, /<string>\/Users\/Test User\/\.npm-global\/bin\/gipity<\/string>/);
93
93
  });
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `~/.gipity/relay.json` state module device info, allowlist, pause flag.
2
+ * `~/.gipity/relay.json` state module - device info, allowlist, pause flag.
3
3
  * Uses HOME to sandbox filesystem writes.
4
4
  */
5
5
  import { describe, it } from 'node:test';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Pure unit tests for the relay's stream-json parsing pipeline. No
3
- * daemon, no spawn, no network just the `parseEvent`, `mapEventToEntries`,
3
+ * daemon, no spawn, no network - just the `parseEvent`, `mapEventToEntries`,
4
4
  * and `createLineSplitter` logic. Uses node's built-in test runner.
5
5
  */
6
6
  import { describe, it } from 'node:test';
@@ -167,7 +167,7 @@ describe('createLineSplitter', () => {
167
167
  assert.equal(total, 10 * chunk.length);
168
168
  });
169
169
  });
170
- describe('mapEventToEntries round trip shape matches IngestEntry union', () => {
170
+ describe('mapEventToEntries - round trip shape matches IngestEntry union', () => {
171
171
  it('every emitted entry has a kind field from the declared union', () => {
172
172
  const validKinds = [
173
173
  'attach', 'prompt', 'assistant', 'tool_use', 'tool_result', 'compact', 'system',
@@ -89,7 +89,7 @@ describe('readBaseline', () => {
89
89
  assert.deepEqual(b.files, {});
90
90
  });
91
91
  it('returns empty baseline when projectGuid does not match current project', async () => {
92
- // Someone else's baseline in our folder must not leak.
92
+ // Someone else's baseline in our folder - must not leak.
93
93
  writeFileSync(join(projectDir, '.gipity', 'sync-state.json'), JSON.stringify({
94
94
  projectGuid: 'proj_OTHER',
95
95
  files: { 'leak.txt': { size: 1, mtime: '2024', sha256: 'x', serverVersion: 1 } },
@@ -140,7 +140,7 @@ describe('conflictedCopyName', () => {
140
140
  });
141
141
  });
142
142
  // ─── sync() end-to-end with fetch mock ────────────────────────
143
- describe('sync() fetch-intercepted', () => {
143
+ describe('sync() - fetch-intercepted', () => {
144
144
  it('noop when local, remote, and baseline all agree', async () => {
145
145
  // Baseline says we have foo.txt at v=3, sha=same.
146
146
  writeFileSync(join(projectDir, 'foo.txt'), 'content');
@@ -167,7 +167,7 @@ describe('sync() — fetch-intercepted', () => {
167
167
  assert.equal(result.applied, 0);
168
168
  assert.equal(result.plan.actions.length, 0);
169
169
  assert.deepEqual(result.errors, []);
170
- // Baseline's lastFullSync must be bumped even when no actions fired
170
+ // Baseline's lastFullSync must be bumped even when no actions fired -
171
171
  // otherwise we can't tell "sync ran and everything was fine" from
172
172
  // "sync never ran".
173
173
  const bl = readBaseline('proj_apply');
@@ -231,7 +231,7 @@ describe('sync() — fetch-intercepted', () => {
231
231
  }));
232
232
  stubFetch(async (url) => {
233
233
  if (url.includes('/files/tree') && !url.includes('content=tar')) {
234
- // Remote has all 20 files (baseline state) client deleted all 20 locally.
234
+ // Remote has all 20 files (baseline state) - client deleted all 20 locally.
235
235
  const data = Object.entries(files).map(([path, e]) => ({
236
236
  path, size: 1, modified: '2024', type: 'file',
237
237
  guid: `fl_${path}`, contentHash: e.sha256, serverVersion: e.serverVersion,
@@ -375,7 +375,7 @@ describe('sync() — fetch-intercepted', () => {
375
375
  });
376
376
  it('apply-time 409 (baseline fresh but another client raced in between) → re-plans as conflict', async () => {
377
377
  // Plan sees: local='modified', remote='unchanged' → upload with CAS=baseline.
378
- // But server has already moved on between manifest-fetch and upload returns
378
+ // But server has already moved on between manifest-fetch and upload - returns
379
379
  // 409. This exercises the apply-phase UploadConflictError handler in sync.ts.
380
380
  const { createHash } = await import('crypto');
381
381
  const baselineSha = createHash('sha256').update('base').digest('hex');
@@ -391,7 +391,7 @@ describe('sync() — fetch-intercepted', () => {
391
391
  }));
392
392
  let initCalls = 0;
393
393
  stubFetch(async (url, init) => {
394
- // Manifest still shows the "unchanged" remote that matches baseline
394
+ // Manifest still shows the "unchanged" remote that matches baseline -
395
395
  // client will plan an upload with expected=3.
396
396
  if (url.includes('/files/tree') && !url.includes('content=tar')) {
397
397
  return new Response(JSON.stringify({ data: [
@@ -404,7 +404,7 @@ describe('sync() — fetch-intercepted', () => {
404
404
  initCalls++;
405
405
  const body = JSON.parse(init.body);
406
406
  if (body.path === 'race.txt' && body.expected_server_version === 3) {
407
- // Server has moved past 3 return 409.
407
+ // Server has moved past 3 - return 409.
408
408
  return new Response(JSON.stringify({
409
409
  error: { code: 'CONFLICT', message: 'Version mismatch: expected 3, current 5' },
410
410
  data: { current_server_version: 5 },
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Advisory sync lock (`.gipity/sync.lock`) prevents concurrent sync
2
+ * Advisory sync lock (`.gipity/sync.lock`) - prevents concurrent sync
3
3
  * processes in the same project dir from corrupting the baseline manifest.
4
4
  */
5
5
  import { describe, it, before, after, beforeEach } from 'node:test';
@@ -63,7 +63,7 @@ describe('acquireLock', () => {
63
63
  });
64
64
  it('breaks a stale lock whose PID is dead', async () => {
65
65
  const lockFile = join(tempProject, '.gipity', 'sync.lock');
66
- // PID 1 exists but is init but PID 999999 is almost certainly dead.
66
+ // PID 1 exists but is init - but PID 999999 is almost certainly dead.
67
67
  // Find a definitely-dead PID by forking a process that exits instantly.
68
68
  const { execSync } = await import('child_process');
69
69
  const deadPid = parseInt(execSync('bash -c "(echo $$; exec sleep 0.01) & wait $!; echo $!"', { encoding: 'utf-8' })
@@ -89,10 +89,10 @@ describe('acquireLock', () => {
89
89
  const { acquireLock } = await import('../sync.js');
90
90
  // First holder.
91
91
  const releaseA = await acquireLock();
92
- // Second caller must wait start its promise, then release A, then await.
92
+ // Second caller must wait - start its promise, then release A, then await.
93
93
  let bResolved = false;
94
94
  const pB = acquireLock().then(r => { bResolved = true; return r; });
95
- // Give B a moment to start polling it should NOT be resolved yet.
95
+ // Give B a moment to start polling - it should NOT be resolved yet.
96
96
  await new Promise(r => setTimeout(r, 100));
97
97
  assert.equal(bResolved, false, 'second acquireLock should still be waiting');
98
98
  releaseA();
@@ -10,7 +10,7 @@ function local(size = 100, sha) {
10
10
  function baselineOf(sha, sv = 1, size = 100) {
11
11
  return { size, mtime: '2024-01-01', sha256: sha, serverVersion: sv };
12
12
  }
13
- describe('plan() 9-cell decision table', () => {
13
+ describe('plan() - 9-cell decision table', () => {
14
14
  it('unchanged × unchanged → noop (no action)', () => {
15
15
  const p = plan(new Map([['foo', local(100, 'h1')]]), new Map([['foo', remote('foo', 'h1', 5)]]), { foo: baselineOf('h1', 5) });
16
16
  assert.equal(p.actions.length, 0);
@@ -84,7 +84,7 @@ describe('plan() — 9-cell decision table', () => {
84
84
  assert.equal(p.actions[0].kind, 'download');
85
85
  });
86
86
  });
87
- describe('plan() summary counts', () => {
87
+ describe('plan() - summary counts', () => {
88
88
  it('counts uploads, downloads, conflicts, deletes correctly', () => {
89
89
  const p = plan(new Map([
90
90
  ['add', local(100, 'a1')], // → upload
@@ -49,7 +49,7 @@ describe('state + settings (with isolated HOME)', () => {
49
49
  // by computing paths ourselves and only using state.ts's pure functions.
50
50
  it('readState returns defaults when no file exists', async () => {
51
51
  const mod = await import(`../updater/state.js?cachebust=${Date.now()}`);
52
- // GIPITY_DIR may be cached to original HOME force-create the path the
52
+ // GIPITY_DIR may be cached to original HOME - force-create the path the
53
53
  // module is actually using, then assert defaults shape.
54
54
  const s = mod.readState();
55
55
  assert.equal(typeof s.lastCheckAt, 'number');
@@ -20,7 +20,7 @@ export declare function scanForAdoption(cwd: string): AdoptScan;
20
20
  * offer the "what would you like to build?" prompt so Claude can scaffold
21
21
  * in place. */
22
22
  export declare function isLikelyEmpty(cwd: string): boolean;
23
- /** Hide-list for "Use this directory" places where adopting cwd as a
23
+ /** Hide-list for "Use this directory" - places where adopting cwd as a
24
24
  * project is obviously wrong. Exact-match only; subdirectories are fine
25
25
  * (e.g. `/tmp/scratch` is allowed, just not `/tmp` itself). */
26
26
  export declare function canAdoptCwd(cwd: string): boolean;
@@ -41,7 +41,7 @@ export interface AdoptResult {
41
41
  applied: number;
42
42
  }
43
43
  /** Adopt `cwd` as a Gipity project. Mirrors `gipity init`'s flow:
44
- * 1. Slug = slugify(basename(cwd)) provided by caller (so caller can
44
+ * 1. Slug = slugify(basename(cwd)) - provided by caller (so caller can
45
45
  * validate/prompt as needed).
46
46
  * 2. Try to match an existing server project by slug; adopt if found.
47
47
  * 3. Otherwise POST /projects with relay-device wiring.
package/dist/adopt-cwd.js CHANGED
@@ -4,9 +4,9 @@
4
4
  * directory" picker option.
5
5
  *
6
6
  * The whole flow:
7
- * 1. `scanForAdoption(cwd)` bound the size to keep us from sucking in a
7
+ * 1. `scanForAdoption(cwd)` - bound the size to keep us from sucking in a
8
8
  * huge monorepo or someone's $HOME by accident.
9
- * 2. `adoptCurrentDir(...)` find-or-create a server project keyed by the
9
+ * 2. `adoptCurrentDir(...)` - find-or-create a server project keyed by the
10
10
  * cwd's slugified basename, then `finalizeLocalProject` to write
11
11
  * `.gipity.json`, sync, and install hooks/skills/gitignore.
12
12
  */
@@ -89,13 +89,13 @@ export function isLikelyEmpty(cwd) {
89
89
  }
90
90
  return entries.every(isSyncIgnored);
91
91
  }
92
- /** Hide-list for "Use this directory" places where adopting cwd as a
92
+ /** Hide-list for "Use this directory" - places where adopting cwd as a
93
93
  * project is obviously wrong. Exact-match only; subdirectories are fine
94
94
  * (e.g. `/tmp/scratch` is allowed, just not `/tmp` itself). */
95
95
  export function canAdoptCwd(cwd) {
96
96
  const norm = resolve(cwd);
97
97
  const home = resolve(homedir());
98
- // Exact root/home/system paths adopting these would scoop the world.
98
+ // Exact root/home/system paths - adopting these would scoop the world.
99
99
  const blocked = new Set([
100
100
  sep,
101
101
  home,
@@ -107,7 +107,7 @@ export function canAdoptCwd(cwd) {
107
107
  return false;
108
108
  // Workspace-parent heuristic: a directory at depth ≤1 below $HOME that
109
109
  // contains 3+ subdirectories with their own `.git/` is almost certainly
110
- // a parent like `~/Github/` not a single project.
110
+ // a parent like `~/Github/` - not a single project.
111
111
  if (norm.startsWith(home + sep)) {
112
112
  const depth = norm.slice(home.length + 1).split(sep).length;
113
113
  if (depth <= 1 && countGitRepoChildren(norm) >= 3)
@@ -116,7 +116,7 @@ export function canAdoptCwd(cwd) {
116
116
  return true;
117
117
  }
118
118
  /** Count immediate subdirectories of `dir` that contain a `.git/` entry.
119
- * Bounded to first 50 entries enough to flag a workspace dir without
119
+ * Bounded to first 50 entries - enough to flag a workspace dir without
120
120
  * walking everything. */
121
121
  function countGitRepoChildren(dir) {
122
122
  let entries;
@@ -178,7 +178,7 @@ export function formatBytes(n) {
178
178
  return `${(n / (1024 * 1024 * 1024)).toFixed(2)} GB`;
179
179
  }
180
180
  /** Adopt `cwd` as a Gipity project. Mirrors `gipity init`'s flow:
181
- * 1. Slug = slugify(basename(cwd)) provided by caller (so caller can
181
+ * 1. Slug = slugify(basename(cwd)) - provided by caller (so caller can
182
182
  * validate/prompt as needed).
183
183
  * 2. Try to match an existing server project by slug; adopt if found.
184
184
  * 3. Otherwise POST /projects with relay-device wiring.
@@ -194,7 +194,7 @@ export async function adoptCurrentDir(opts) {
194
194
  project = res.data.find(p => p.slug === opts.projectSlug) || null;
195
195
  }
196
196
  catch {
197
- // List failed fall through to POST.
197
+ // List failed - fall through to POST.
198
198
  }
199
199
  let isNew = false;
200
200
  if (!project) {
@@ -215,7 +215,7 @@ export async function adoptCurrentDir(opts) {
215
215
  catch (err) {
216
216
  if (err instanceof ApiError && err.statusCode === 409) {
217
217
  // Race: re-fetch and adopt the conflicting one (someone else just
218
- // took the slug likely the same user from another tab).
218
+ // took the slug - likely the same user from another tab).
219
219
  const res = await get('/projects?limit=100');
220
220
  const found = res.data.find(p => p.slug === opts.projectSlug);
221
221
  if (!found)
package/dist/api.d.ts CHANGED
@@ -24,7 +24,7 @@ export declare function download(path: string): Promise<Buffer>;
24
24
  /** Download a response as a Node.js Readable stream */
25
25
  export declare function downloadStream(path: string): Promise<import('stream').Readable>;
26
26
  /**
27
- * PUT raw bytes to a presigned URL (no auth header the URL is signed).
27
+ * PUT raw bytes to a presigned URL (no auth header - the URL is signed).
28
28
  * Supports a Buffer or a Readable stream body. Returns the response ETag header
29
29
  * (without quotes), used for multipart upload completion.
30
30
  */
package/dist/api.js CHANGED
@@ -141,7 +141,7 @@ export async function downloadStream(path) {
141
141
  return Readable.fromWeb(res.body);
142
142
  }
143
143
  /**
144
- * PUT raw bytes to a presigned URL (no auth header the URL is signed).
144
+ * PUT raw bytes to a presigned URL (no auth header - the URL is signed).
145
145
  * Supports a Buffer or a Readable stream body. Returns the response ETag header
146
146
  * (without quotes), used for multipart upload completion.
147
147
  */
package/dist/auth.js CHANGED
@@ -83,7 +83,7 @@ export async function refreshTokenIfNeeded() {
83
83
  });
84
84
  }
85
85
  catch {
86
- // Refresh failed caller will see expired auth
86
+ // Refresh failed - caller will see expired auth
87
87
  }
88
88
  }
89
89
  //# sourceMappingURL=auth.js.map