gipity 1.1.5 → 1.1.7
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/agents/agy.js +52 -0
- package/dist/agents/index.js +2 -0
- package/dist/api.js +19 -3
- package/dist/capture/sources/agy.js +88 -0
- package/dist/catalog.js +2 -2
- package/dist/client-context.js +9 -0
- package/dist/commands/brand.js +125 -0
- package/dist/commands/build.js +1 -1
- package/dist/commands/db.js +61 -0
- package/dist/commands/fn.js +2 -8
- package/dist/commands/init.js +2 -2
- package/dist/commands/key.js +91 -0
- package/dist/commands/page-eval.js +70 -3
- package/dist/commands/page-screenshot.js +34 -6
- package/dist/commands/records.js +93 -23
- package/dist/commands/service.js +71 -7
- package/dist/commands/token.js +6 -1
- package/dist/commands/uninstall.js +14 -1
- package/dist/commands/workflow.js +59 -6
- package/dist/db-checkpoint.js +42 -0
- package/dist/hooks/capture-runner.js +20 -8
- package/dist/index.js +777 -137
- package/dist/knowledge.js +3 -3
- package/dist/project-setup.js +2 -0
- package/dist/relay/diagnostics.js +4 -2
- package/dist/relay/onboarding.js +3 -3
- package/dist/setup.js +198 -16
- package/dist/template-vars.js +74 -0
- package/dist/utils.js +16 -5
- package/package.json +2 -2
package/dist/knowledge.js
CHANGED
|
@@ -106,7 +106,7 @@ mkdir -p ~/GipityProjects/<slug> && cd ~/GipityProjects/<slug> && gipity init <s
|
|
|
106
106
|
|
|
107
107
|
## CLI quick reference
|
|
108
108
|
|
|
109
|
-
Key commands: \`gipity add <template|kit>\`, \`gipity deploy dev\`, \`gipity sandbox run\`, \`gipity page inspect <url>\`, \`gipity page screenshot <url>\`, \`gipity db query "SQL"\`, \`gipity fn call <name>\`, \`gipity logs fn <name>\`, \`gipity secrets set <NAME> <value> [--account]\` (store an API key/token encrypted; read in functions via \`secrets.get('NAME')\` — never hardcode keys), \`gipity email send --to <addr> --subject <s> --body <b>\` (sends as \`gipity@gipity.ai\`; omit \`--to\` to self-send), \`gipity skill read <name>\`.
|
|
109
|
+
Key commands: \`gipity add <template|kit>\`, \`gipity brand set --emoji <e>|--color <hex>\` (regenerate the app's icons + social share card), \`gipity deploy dev\`, \`gipity sandbox run\`, \`gipity page inspect <url>\`, \`gipity page screenshot <url>\`, \`gipity db query "SQL"\`, \`gipity fn call <name>\`, \`gipity logs fn <name>\`, \`gipity secrets set <NAME> <value> [--account]\` (store an API key/token encrypted; read in functions via \`secrets.get('NAME')\` — never hardcode keys), \`gipity email send --to <addr> --subject <s> --body <b>\` (sends as \`gipity@gipity.ai\`; omit \`--to\` to self-send), \`gipity skill read <name>\`.
|
|
110
110
|
Rename for findability: \`gipity project rename <name>\` renames the current project's display name (the slug and deployed URLs never change); \`gipity chat rename <title>\` renames the current chat's tab title. Both are the display label users scan to switch between tabs — retitle a chat when the conversation clearly shifts to a new topic (sparingly, not every turn), and keep every project/chat title SHORT: 2-4 words, ≤40 characters, no trailing punctuation (e.g. "Stripe checkout", "Tetris game").
|
|
111
111
|
Pull an existing remote project local (given its URL/slug): \`mkdir -p ~/GipityProjects/<slug> && cd ~/GipityProjects/<slug> && gipity init <slug>\` (adopts the matching project and syncs files down - this is the "clone").
|
|
112
112
|
Move whole apps in/out: \`gipity save\` (export this project as a portable \`.gip\` bundle), \`gipity load <file.gip | github:owner/repo>\` (import as a NEW project; \`--inspect\` to preview), \`gipity github connect\` (1-2 click GitHub access for imports). Porting a Vercel/Replit/Lovable app? Load the \`app-import\` skill first.
|
|
@@ -116,7 +116,7 @@ Run \`gipity --help\` for the full list. Use \`--help\` on any command for detai
|
|
|
116
116
|
|
|
117
117
|
Function return shape: \`gipity fn call\`, the in-test \`ctx.fn.call\`/\`callAs\`, and the client \`Gipity.fn\` all return your function's value **unwrapped** - read/assert \`result.field\`. Only raw HTTP/\`curl\` wraps it as \`{ data: ... }\`; never write \`result.data.field\` in a test.
|
|
118
118
|
|
|
119
|
-
Tests are isolated, not run against your live DB: \`gipity test\` points \`ctx.fn.call\`/\`callAs\` at a throwaway copy of your database (your \`migrations/\` + \`seeds/\`), reset (truncate + reseed) before
|
|
119
|
+
Tests are isolated, not run against your live DB: \`gipity test\` points \`ctx.fn.call\`/\`callAs\` at a throwaway copy of your database (your \`migrations/\` + \`seeds/\`), one per test FILE, reset (truncate + reseed) before the file runs - so files can't interfere, test rows never reach the deployed app, and you don't write teardown. Functions see \`ctx.isTest === true\` during a run (use it to skip your own rate limiting); the platform also suppresses \`notify()\` push so a suite can't spam subscribers. Reference data tests need goes in seed files; a runtime-written settings table that isn't seeded goes under \`test.preserve\` in \`gipity.yaml\`. Build per-file fixtures in \`setup(fn)\` → \`ctx.fixtures\`.
|
|
120
120
|
|
|
121
121
|
## Hit friction on the platform? Report it in real time
|
|
122
122
|
|
|
@@ -187,7 +187,7 @@ App development skills:
|
|
|
187
187
|
- \`app-development\` - functions, database, and API
|
|
188
188
|
- \`app-import\` - import apps from GitHub/.gip bundles (incl. Vercel/Replit/Lovable porting) and export any project as a portable .gip - app_import tool, gipity save/load
|
|
189
189
|
- \`app-records\` - Gipity Records: declared tables → validated CRUD, provenance, workflows, auto UI
|
|
190
|
-
- \`app-testing\` - testing deployed app functions (ctx.fn.call/callAs, the
|
|
190
|
+
- \`app-testing\` - testing deployed app functions (ctx.fn.call/callAs, the per-file test DB)
|
|
191
191
|
- \`deploy\` - the deploy pipeline & gipity.yaml manifest
|
|
192
192
|
- \`jobs\` - long-running CPU + GPU compute jobs (Python / Node / bash)
|
|
193
193
|
- \`realtime-scheduled-app\` - recipe: realtime presence/messages + DB function + scheduled poster, end-to-end
|
package/dist/project-setup.js
CHANGED
|
@@ -36,6 +36,8 @@ export async function finalizeLocalProject(opts) {
|
|
|
36
36
|
const sub = await substituteDir(opts.dir, {
|
|
37
37
|
projectGuid: opts.projectGuid,
|
|
38
38
|
projectName: opts.projectName,
|
|
39
|
+
accountSlug: opts.accountSlug,
|
|
40
|
+
projectSlug: opts.projectSlug,
|
|
39
41
|
});
|
|
40
42
|
if (sub.changed.length) {
|
|
41
43
|
console.log(muted(`Resolved template vars in ${sub.changed.length} file${sub.changed.length > 1 ? 's' : ''}.`));
|
|
@@ -137,8 +137,8 @@ export async function collectDiagnostics() {
|
|
|
137
137
|
} })();
|
|
138
138
|
// Run the subprocess probes concurrently (each already bounded + best-effort)
|
|
139
139
|
// so the whole snapshot costs one timeout, not the sum of four.
|
|
140
|
-
const [claude, codex, grok, cursor, gpu] = await Promise.all([
|
|
141
|
-
probeVersion('claude'), probeVersion('codex'), probeVersion('grok'), probeVersion('cursor'), detectGpu(),
|
|
140
|
+
const [claude, codex, grok, agy, cursor, gpu] = await Promise.all([
|
|
141
|
+
probeVersion('claude'), probeVersion('codex'), probeVersion('grok'), probeVersion('agy'), probeVersion('cursor'), detectGpu(),
|
|
142
142
|
]);
|
|
143
143
|
const agents = {};
|
|
144
144
|
if (claude)
|
|
@@ -147,6 +147,8 @@ export async function collectDiagnostics() {
|
|
|
147
147
|
agents.codex = codex;
|
|
148
148
|
if (grok)
|
|
149
149
|
agents.grok = grok;
|
|
150
|
+
if (agy)
|
|
151
|
+
agents.agy = agy;
|
|
150
152
|
if (cursor)
|
|
151
153
|
agents.cursor = cursor;
|
|
152
154
|
return {
|
package/dist/relay/onboarding.js
CHANGED
|
@@ -82,7 +82,7 @@ export async function runRelaySetup(opts) {
|
|
|
82
82
|
const promptText = opts.mode === 'run-now'
|
|
83
83
|
? ' Set up remote control on this computer'
|
|
84
84
|
: ' Enable remote control';
|
|
85
|
-
const enable = await confirm(promptText, { default: 'yes' });
|
|
85
|
+
const enable = await confirm(promptText, { default: 'yes', headless: 'no' });
|
|
86
86
|
if (!enable) {
|
|
87
87
|
state.setRelayEnabled(false);
|
|
88
88
|
console.log(` ${muted('Skipped. Turn on later with')} ${brand('gipity connect')}${muted('.')}`);
|
|
@@ -119,7 +119,7 @@ export async function runRelaySetup(opts) {
|
|
|
119
119
|
startDaemon();
|
|
120
120
|
console.log('');
|
|
121
121
|
// Offer OS-level autostart (launchd / systemd --user / Task Scheduler).
|
|
122
|
-
const autostartOs = await confirm(' Also start at OS login', { default: 'yes' });
|
|
122
|
+
const autostartOs = await confirm(' Also start at OS login', { default: 'yes', headless: 'no' });
|
|
123
123
|
if (autostartOs) {
|
|
124
124
|
try {
|
|
125
125
|
const res = installAutostart();
|
|
@@ -153,7 +153,7 @@ export async function runRelaySetup(opts) {
|
|
|
153
153
|
// on|off` flips it later.)
|
|
154
154
|
if (state.getDiagnosticsConsent() === undefined) {
|
|
155
155
|
console.log('');
|
|
156
|
-
const diag = await confirm(' Share anonymous diagnostics info', { default: 'yes' });
|
|
156
|
+
const diag = await confirm(' Share anonymous diagnostics info', { default: 'yes', headless: 'no' });
|
|
157
157
|
state.setDiagnosticsConsent(diag);
|
|
158
158
|
}
|
|
159
159
|
console.log('');
|
package/dist/setup.js
CHANGED
|
@@ -32,6 +32,7 @@ export const PRIMER_FILES = {
|
|
|
32
32
|
claude: 'CLAUDE.md',
|
|
33
33
|
codex: 'AGENTS.md',
|
|
34
34
|
grok: 'AGENTS.md', // Grok Build reads the AGENTS.md family (and CLAUDE.md) natively
|
|
35
|
+
agy: 'AGENTS.md', // Antigravity reads the same AGENTS.md/GEMINI.md rules family natively
|
|
35
36
|
aider: 'AGENTS.md', // shares the Codex primer; aider is pointed at it via .aider.conf.yml
|
|
36
37
|
gemini: 'GEMINI.md',
|
|
37
38
|
copilot: '.github/copilot-instructions.md',
|
|
@@ -58,7 +59,7 @@ export const AIDER_CONF_FILE = '.aider.conf.yml';
|
|
|
58
59
|
* `ignore` package in config.ts. */
|
|
59
60
|
export const SCRATCH_IGNORE = ['tmp/', '.tmp/', '*_tmp/', '.gipityscratch/'];
|
|
60
61
|
export const DEFAULT_SYNC_IGNORE = [
|
|
61
|
-
'node_modules', '.git', '.gipity.json', '.gipity/', '.claude/', '.codex/', '.gitignore', AIDER_CONF_FILE,
|
|
62
|
+
'node_modules', '.git', '.gipity.json', '.gipity/', '.claude/', '.codex/', '.agents/', '.gitignore', AIDER_CONF_FILE,
|
|
62
63
|
// Home-directory junk: a project created inside a real home dir (or one that
|
|
63
64
|
// shells out) sweeps in a cache dir + shell dotfiles that are never app
|
|
64
65
|
// files. `.cache/` alone can be gigabytes (it was 2.4 GB on one project),
|
|
@@ -398,9 +399,16 @@ export const AGENT_HOOKS_DIR = join(homedir(), '.gipity', 'agent-hooks');
|
|
|
398
399
|
* version and the exact skill names copied, so upgrades replace and uninstall
|
|
399
400
|
* removes precisely those. */
|
|
400
401
|
export const AGENT_SKILLS_MANIFEST = join(homedir(), '.gipity', 'agent-skills.json');
|
|
401
|
-
|
|
402
|
+
// Antigravity (agy) reads skills from its own global customization root,
|
|
403
|
+
// ~/.gemini/config/skills/ (confirmed against agy's own customization-system
|
|
404
|
+
// docs) - NOT the cross-agent ~/.agents/skills Codex/OpenClaw-family tools
|
|
405
|
+
// share. Same manifest pattern, separate directory + file so upgrades and
|
|
406
|
+
// uninstall touch exactly the skills each tool actually reads.
|
|
407
|
+
export const AGY_SKILLS_DIR = join(homedir(), '.gemini', 'config', 'skills');
|
|
408
|
+
export const AGY_SKILLS_MANIFEST = join(homedir(), '.gipity', 'agy-skills.json');
|
|
409
|
+
function skillsManifestState(manifestPath) {
|
|
402
410
|
try {
|
|
403
|
-
const m = JSON.parse(readFileSync(
|
|
411
|
+
const m = JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
404
412
|
return {
|
|
405
413
|
current: typeof m?.version === 'string' && versionGte(m.version, GIPITY_PLUGIN_VERSION),
|
|
406
414
|
skills: Array.isArray(m?.skills) ? m.skills : [],
|
|
@@ -410,15 +418,21 @@ export function agentSkillsState() {
|
|
|
410
418
|
return { current: false, skills: [] };
|
|
411
419
|
}
|
|
412
420
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
421
|
+
export function agentSkillsState() {
|
|
422
|
+
return skillsManifestState(AGENT_SKILLS_MANIFEST);
|
|
423
|
+
}
|
|
424
|
+
export function agySkillsState() {
|
|
425
|
+
return skillsManifestState(AGY_SKILLS_MANIFEST);
|
|
426
|
+
}
|
|
427
|
+
/** Shared core: clone GipityAI/skills, copy every skill dir with a SKILL.md
|
|
428
|
+
* into `skillsDir`, stage the plugin's hook scripts into ~/.gipity/agent-hooks
|
|
429
|
+
* (shared infra - launch.sh/capture.cjs/sync-push.cjs are agent-agnostic, so
|
|
430
|
+
* every caller re-stages them harmlessly), and record what was installed in
|
|
431
|
+
* `manifestPath`. Source of truth is the same GipityAI/skills repo the
|
|
432
|
+
* Claude/Grok plugin installs clone - fetched with a shallow git clone into a
|
|
433
|
+
* temp dir. Best-effort: no git, no network, or a failed clone all leave
|
|
434
|
+
* things as they were; the next init retries. */
|
|
435
|
+
function installSkillsAndHooks(skillsDir, manifestPath, harnessLabel) {
|
|
422
436
|
if (!binaryOnPath('git'))
|
|
423
437
|
return;
|
|
424
438
|
const tmp = mkdtempSync(join(tmpdir(), 'gipity-skills-'));
|
|
@@ -441,8 +455,8 @@ export function ensureAgentSkillsInstalled() {
|
|
|
441
455
|
continue;
|
|
442
456
|
if (!existsSync(join(skillsSrc, entry.name, 'SKILL.md')))
|
|
443
457
|
continue;
|
|
444
|
-
mkdirSync(
|
|
445
|
-
cpSync(join(skillsSrc, entry.name), join(
|
|
458
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
459
|
+
cpSync(join(skillsSrc, entry.name), join(skillsDir, entry.name), {
|
|
446
460
|
recursive: true,
|
|
447
461
|
force: true,
|
|
448
462
|
});
|
|
@@ -452,14 +466,27 @@ export function ensureAgentSkillsInstalled() {
|
|
|
452
466
|
for (const script of readdirSync(join(repo, 'hooks', 'scripts'))) {
|
|
453
467
|
cpSync(join(repo, 'hooks', 'scripts', script), join(AGENT_HOOKS_DIR, script), { force: true });
|
|
454
468
|
}
|
|
455
|
-
writeFileSync(
|
|
456
|
-
console.log(`Installed ${names.length} Gipity skills for
|
|
469
|
+
writeFileSync(manifestPath, JSON.stringify({ version, skills: names }, null, 2) + '\n');
|
|
470
|
+
console.log(`Installed ${names.length} Gipity skills for ${harnessLabel} (${skillsDir}).`);
|
|
457
471
|
}
|
|
458
472
|
catch { /* best-effort - never break setup */ }
|
|
459
473
|
finally {
|
|
460
474
|
rmSync(tmp, { recursive: true, force: true });
|
|
461
475
|
}
|
|
462
476
|
}
|
|
477
|
+
export function ensureAgentSkillsInstalled() {
|
|
478
|
+
if (agentSkillsState().current)
|
|
479
|
+
return;
|
|
480
|
+
installSkillsAndHooks(AGENTS_SKILLS_DIR, AGENT_SKILLS_MANIFEST, 'Codex');
|
|
481
|
+
}
|
|
482
|
+
/** Materialize the Gipity skills into Antigravity's global skill root. Mirrors
|
|
483
|
+
* ensureAgentSkillsInstalled() but targets agy's own directory - see the
|
|
484
|
+
* AGY_SKILLS_DIR comment above for why it differs from Codex's. */
|
|
485
|
+
export function ensureAgySkillsInstalled() {
|
|
486
|
+
if (agySkillsState().current)
|
|
487
|
+
return;
|
|
488
|
+
installSkillsAndHooks(AGY_SKILLS_DIR, AGY_SKILLS_MANIFEST, 'Antigravity');
|
|
489
|
+
}
|
|
463
490
|
/** Pure core of the .codex/hooks.json merge: given the file's current content
|
|
464
491
|
* (`null` when absent), return the new content, or `null` when no change is
|
|
465
492
|
* needed. Adds the Gipity sync hook groups (push on file edits, pull before
|
|
@@ -535,6 +562,160 @@ export function setupCodexIntegration() {
|
|
|
535
562
|
ensureAgentSkillsInstalled();
|
|
536
563
|
setupCodexHooks();
|
|
537
564
|
}
|
|
565
|
+
// --- Google Antigravity (agy) --------------------------------------------
|
|
566
|
+
// agy's hook system is a real departure from the Claude-format hooks Codex
|
|
567
|
+
// and Grok reuse: project hooks live in `.agents/hooks.json` as a NAMED-block
|
|
568
|
+
// object (not a flat `hooks` key), tool-scoped events (PreToolUse/PostToolUse)
|
|
569
|
+
// require a `matcher` regex, and every hook command must print a JSON object
|
|
570
|
+
// on stdout.
|
|
571
|
+
//
|
|
572
|
+
// Deliberately NOT wired: PreToolUse. Gipity only needs to OBSERVE tool calls
|
|
573
|
+
// (capture) and react after a write lands (sync-push) - it has no reason to
|
|
574
|
+
// gate them. A PreToolUse hook that answers `{"decision":"allow"}` genuinely
|
|
575
|
+
// overrides agy's own approval prompt (confirmed against agy's own hooks
|
|
576
|
+
// contract: `"allow"` means "automatically allow the tool execution"), so
|
|
577
|
+
// registering one would auto-approve every tool agy runs - including shell
|
|
578
|
+
// commands - in every Gipity-linked project, in BOTH headless and interactive
|
|
579
|
+
// sessions. Confirmed live that headless `-p` writes succeed with no
|
|
580
|
+
// PreToolUse hook at all, with or without `--dangerously-skip-permissions` -
|
|
581
|
+
// so there is no capture/sync reason to have one, and Claude/Codex don't
|
|
582
|
+
// silently touch agent approval either. PostToolUse/Stop route through a
|
|
583
|
+
// small wrapper script (AGY_HOOKS_SCRIPT below) that does the real work
|
|
584
|
+
// (sync-push + session capture) and then unconditionally prints `{}` - not
|
|
585
|
+
// decision-gated, so degrading silently on a node-resolution failure is the
|
|
586
|
+
// same acceptable risk every other harness's hooks already carry.
|
|
587
|
+
/** Written verbatim into ~/.gipity/agent-hooks/agy-hooks.cjs by setupAgyHooks().
|
|
588
|
+
* Unlike Codex's hook scripts (cloned from the GipityAI/skills repo), this
|
|
589
|
+
* file is authored by the CLI itself - agy is not part of that repo's plugin
|
|
590
|
+
* ecosystem, just a consumer of the same shared sync-push.cjs/capture.cjs.
|
|
591
|
+
*
|
|
592
|
+
* post-tool-use: reads agy's PostToolUse payload (which - confirmed live -
|
|
593
|
+
* carries `toolCall` directly, unlike agy's own docs suggest) and, for a
|
|
594
|
+
* file-write tool, synthesizes a Claude-Code-shaped `{tool_input:{file_path}}`
|
|
595
|
+
* payload for the UNMODIFIED sync-push.cjs (which only knows Claude/Grok's
|
|
596
|
+
* field names) - then always forwards the raw payload to capture.cjs for
|
|
597
|
+
* session mirroring (its normalizeHookInput already reads agy's camelCase
|
|
598
|
+
* conversationId/transcriptPath fields).
|
|
599
|
+
* stop: forwards the raw payload to capture.cjs for a final flush.
|
|
600
|
+
* Either way, stdout is always `{}` - agy isn't gating on this response. */
|
|
601
|
+
const AGY_HOOKS_SCRIPT = `#!/usr/bin/env node
|
|
602
|
+
'use strict';
|
|
603
|
+
const { spawnSync } = require('child_process');
|
|
604
|
+
const { join } = require('path');
|
|
605
|
+
|
|
606
|
+
const WRITE_TOOLS = new Set(['write_to_file', 'replace_file_content']);
|
|
607
|
+
|
|
608
|
+
function readStdin() {
|
|
609
|
+
return new Promise((res) => {
|
|
610
|
+
let data = '';
|
|
611
|
+
process.stdin.setEncoding('utf-8');
|
|
612
|
+
process.stdin.on('data', (c) => { data += c; });
|
|
613
|
+
process.stdin.on('end', () => res(data));
|
|
614
|
+
process.stdin.on('error', () => res(data));
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
async function main() {
|
|
619
|
+
const event = process.argv[2];
|
|
620
|
+
const raw = await readStdin();
|
|
621
|
+
let payload = {};
|
|
622
|
+
try { payload = JSON.parse(raw); } catch { /* keep {} */ }
|
|
623
|
+
|
|
624
|
+
try {
|
|
625
|
+
if (event === 'post-tool-use') {
|
|
626
|
+
const toolCall = payload.toolCall;
|
|
627
|
+
const filePath = toolCall && toolCall.args && toolCall.args.TargetFile;
|
|
628
|
+
if (toolCall && WRITE_TOOLS.has(toolCall.name) && typeof filePath === 'string' && filePath) {
|
|
629
|
+
spawnSync(process.execPath, [join(__dirname, 'sync-push.cjs')], {
|
|
630
|
+
input: JSON.stringify({ tool_input: { file_path: filePath } }),
|
|
631
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
632
|
+
windowsHide: true,
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
spawnSync(process.execPath, [join(__dirname, 'capture.cjs'), 'agy', 'post-tool-use'], {
|
|
636
|
+
input: raw,
|
|
637
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
638
|
+
windowsHide: true,
|
|
639
|
+
});
|
|
640
|
+
} else if (event === 'stop') {
|
|
641
|
+
spawnSync(process.execPath, [join(__dirname, 'capture.cjs'), 'agy', 'stop'], {
|
|
642
|
+
input: raw,
|
|
643
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
644
|
+
windowsHide: true,
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
} catch { /* never let a side effect break the response below */ }
|
|
648
|
+
|
|
649
|
+
process.stdout.write('{}');
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
main();
|
|
653
|
+
`;
|
|
654
|
+
/** Pure core of the .agents/hooks.json merge for Antigravity (agy). Given the
|
|
655
|
+
* file's current content (`null` when absent), return the new content, or
|
|
656
|
+
* `null` when no change is needed. Our whole contribution lives under one
|
|
657
|
+
* named key ('gipity'), so a re-run replaces it wholesale (simpler than
|
|
658
|
+
* Codex's per-entry merge, and fine here since nothing else writes into this
|
|
659
|
+
* key) while any other named hook block - the user's own, or another tool's -
|
|
660
|
+
* is preserved untouched. Exported for unit testing. */
|
|
661
|
+
export function applyAgyHooks(existing) {
|
|
662
|
+
const wrapper = join(AGENT_HOOKS_DIR, 'agy-hooks.cjs');
|
|
663
|
+
const launcher = join(AGENT_HOOKS_DIR, 'launch.sh');
|
|
664
|
+
const wrapCmd = (event) => `sh "${launcher}" "${wrapper}" ${event}`;
|
|
665
|
+
const block = {
|
|
666
|
+
PostToolUse: [
|
|
667
|
+
{ matcher: '.*', hooks: [{ type: 'command', command: wrapCmd('post-tool-use'), timeout: 30 }] },
|
|
668
|
+
],
|
|
669
|
+
Stop: [
|
|
670
|
+
{ hooks: [{ type: 'command', command: wrapCmd('stop'), timeout: 60 }] },
|
|
671
|
+
],
|
|
672
|
+
};
|
|
673
|
+
let settings = {};
|
|
674
|
+
if (existing !== null) {
|
|
675
|
+
try {
|
|
676
|
+
settings = JSON.parse(existing);
|
|
677
|
+
}
|
|
678
|
+
catch {
|
|
679
|
+
return null; // user file we can't parse - leave it alone
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
if (JSON.stringify(settings.gipity ?? null) === JSON.stringify(block))
|
|
683
|
+
return null; // already current
|
|
684
|
+
settings.gipity = block;
|
|
685
|
+
return JSON.stringify(settings, null, 2) + '\n';
|
|
686
|
+
}
|
|
687
|
+
/** Write the project-level Antigravity hooks (.agents/hooks.json) and stage
|
|
688
|
+
* the wrapper script it invokes. POSIX only, same constraint as Codex's
|
|
689
|
+
* hooks (the commands run through a POSIX sh launcher). */
|
|
690
|
+
export function setupAgyHooks() {
|
|
691
|
+
if (process.platform === 'win32')
|
|
692
|
+
return;
|
|
693
|
+
const cwd = resolve(process.cwd());
|
|
694
|
+
if (cwd === resolve(homedir()))
|
|
695
|
+
return; // never treat $HOME as a project
|
|
696
|
+
mkdirSync(AGENT_HOOKS_DIR, { recursive: true });
|
|
697
|
+
writeFileSync(join(AGENT_HOOKS_DIR, 'agy-hooks.cjs'), AGY_HOOKS_SCRIPT);
|
|
698
|
+
const path = join(cwd, '.agents', 'hooks.json');
|
|
699
|
+
const existing = existsSync(path) ? readFileSync(path, 'utf-8') : null;
|
|
700
|
+
const next = applyAgyHooks(existing);
|
|
701
|
+
if (next === null)
|
|
702
|
+
return;
|
|
703
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
704
|
+
writeFileSync(path, next);
|
|
705
|
+
console.log(existing === null
|
|
706
|
+
? 'Wrote Antigravity sync + session-capture hooks (.agents/hooks.json).'
|
|
707
|
+
: 'Updated Antigravity hooks (.agents/hooks.json).');
|
|
708
|
+
}
|
|
709
|
+
/** Full Antigravity integration: skills at its own global root + project sync
|
|
710
|
+
* hooks. Gated on the agy binary so machines without it get only the
|
|
711
|
+
* AGENTS.md primer. Unlike Codex, agy needs no one-time hook-approval nudge -
|
|
712
|
+
* confirmed live that project hooks fire without any manual trust step. */
|
|
713
|
+
export function setupAgyIntegration() {
|
|
714
|
+
if (!binaryOnPath('agy'))
|
|
715
|
+
return;
|
|
716
|
+
ensureAgySkillsInstalled();
|
|
717
|
+
setupAgyHooks();
|
|
718
|
+
}
|
|
538
719
|
export function setupClaudeHooks() {
|
|
539
720
|
// All hooks ship in the plugin - enable it at user scope (and clean up any
|
|
540
721
|
// legacy hook blocks in the user-global settings while we're there).
|
|
@@ -746,6 +927,7 @@ export const SUPPORTED_TOOLS = [
|
|
|
746
927
|
{ key: 'claude', label: 'Claude Code (CLAUDE.md + Gipity plugin)', setup: setupClaudeMd, integrate: setupClaudeHooks },
|
|
747
928
|
{ key: 'codex', label: 'OpenAI Codex (AGENTS.md + skills + sync hooks)', setup: setupAgentsMd, integrate: setupCodexIntegration },
|
|
748
929
|
{ key: 'grok', label: 'Grok Build (AGENTS.md + Gipity plugin)', setup: setupAgentsMd, integrate: ensureGrokPluginInstalled },
|
|
930
|
+
{ key: 'agy', label: 'Antigravity (AGENTS.md + skills + sync hooks)', setup: setupAgentsMd, integrate: setupAgyIntegration },
|
|
749
931
|
{ key: 'aider', label: 'Aider (AGENTS.md + .aider.conf.yml)', setup: setupAiderMd, optIn: true },
|
|
750
932
|
{ key: 'gemini', label: 'Gemini CLI (GEMINI.md)', setup: setupGeminiMd },
|
|
751
933
|
{ key: 'copilot', label: 'GitHub Copilot (.github/copilot-instructions.md)', setup: setupCopilotMd },
|
package/dist/template-vars.js
CHANGED
|
@@ -33,6 +33,7 @@ export const KNOWN_PLACEHOLDERS = [
|
|
|
33
33
|
'{{JS_TITLE}}',
|
|
34
34
|
'{{PROJECT_GUID}}',
|
|
35
35
|
'{{DATABASE}}',
|
|
36
|
+
'{{HEAD_BLOCK}}',
|
|
36
37
|
'{{DESCRIPTION_META}}',
|
|
37
38
|
'{{OG_DESCRIPTION}}',
|
|
38
39
|
'{{JSON_LD_BLOCK}}',
|
|
@@ -49,6 +50,76 @@ function escapeHtml(s) {
|
|
|
49
50
|
function jsEscape(s) {
|
|
50
51
|
return s.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
51
52
|
}
|
|
53
|
+
/** Accent palette + hash, mirrored from the server's `services/app-brand.ts`
|
|
54
|
+
* so the {{HEAD_BLOCK}} theme-color this path emits matches what a server
|
|
55
|
+
* install of the same project would emit. Append-only; keep in sync. */
|
|
56
|
+
const ACCENT_PALETTE = [
|
|
57
|
+
'#f59e0b', '#f97316', '#ef4444', '#ec4899', '#a855f7', '#6366f1',
|
|
58
|
+
'#3b82f6', '#0ea5e9', '#14b8a6', '#22c55e', '#84cc16', '#eab308',
|
|
59
|
+
];
|
|
60
|
+
function hashString(s) {
|
|
61
|
+
let h = 0x811c9dc5;
|
|
62
|
+
for (let i = 0; i < s.length; i++) {
|
|
63
|
+
h ^= s.charCodeAt(i);
|
|
64
|
+
h = Math.imul(h, 0x01000193);
|
|
65
|
+
}
|
|
66
|
+
return h >>> 0;
|
|
67
|
+
}
|
|
68
|
+
/** Blend the accent into near-black — theme-color tint (mirrors app-brand.ts). */
|
|
69
|
+
function darkTint(hex) {
|
|
70
|
+
const n = parseInt(hex.slice(1), 16);
|
|
71
|
+
const base = 0x101014;
|
|
72
|
+
const ch = (shift) => {
|
|
73
|
+
const a = (n >> shift) & 0xff;
|
|
74
|
+
const b = (base >> shift) & 0xff;
|
|
75
|
+
return Math.round(b + (a - b) * 0.14).toString(16).padStart(2, '0');
|
|
76
|
+
};
|
|
77
|
+
return `#${ch(16)}${ch(8)}${ch(0)}`;
|
|
78
|
+
}
|
|
79
|
+
/** The shared head every template's index.html carries as {{HEAD_BLOCK}}.
|
|
80
|
+
* Mirrors `buildHeadBlock` in platform `services/app-brand.ts` — same tags,
|
|
81
|
+
* same order, same indent; keep the two in sync. */
|
|
82
|
+
function buildHeadBlock(v) {
|
|
83
|
+
const t = escapeHtml(v.projectName);
|
|
84
|
+
const d = v.description ? escapeHtml(v.description) : '';
|
|
85
|
+
const url = v.accountSlug && v.projectSlug
|
|
86
|
+
? `https://app.gipity.ai/${v.accountSlug}/${v.projectSlug}/`
|
|
87
|
+
: undefined;
|
|
88
|
+
const themeColor = darkTint(ACCENT_PALETTE[hashString(v.projectGuid || v.projectName) % ACCENT_PALETTE.length]);
|
|
89
|
+
const jsonLd = JSON.stringify({
|
|
90
|
+
'@context': 'https://schema.org',
|
|
91
|
+
'@type': 'WebApplication',
|
|
92
|
+
name: v.projectName,
|
|
93
|
+
...(v.description ? { description: v.description } : {}),
|
|
94
|
+
...(url ? { url } : {}),
|
|
95
|
+
}, null, 2).replace(/<\//g, '<\\/');
|
|
96
|
+
const lines = [
|
|
97
|
+
`<title>${t}</title>`,
|
|
98
|
+
...(d ? [`<meta name="description" content="${d}">`] : []),
|
|
99
|
+
...(url ? [`<link rel="canonical" href="${url}">`] : []),
|
|
100
|
+
`<meta property="og:title" content="${t}">`,
|
|
101
|
+
`<meta property="og:type" content="website">`,
|
|
102
|
+
...(d ? [`<meta property="og:description" content="${d}">`] : []),
|
|
103
|
+
...(url ? [
|
|
104
|
+
`<meta property="og:url" content="${url}">`,
|
|
105
|
+
`<meta property="og:image" content="${url}images/og-image.png">`,
|
|
106
|
+
`<meta property="og:image:width" content="1200">`,
|
|
107
|
+
`<meta property="og:image:height" content="630">`,
|
|
108
|
+
] : []),
|
|
109
|
+
`<meta name="twitter:card" content="summary_large_image">`,
|
|
110
|
+
`<meta name="twitter:title" content="${t}">`,
|
|
111
|
+
...(d ? [`<meta name="twitter:description" content="${d}">`] : []),
|
|
112
|
+
...(url ? [`<meta name="twitter:image" content="${url}images/og-image.png">`] : []),
|
|
113
|
+
`<meta name="theme-color" content="${themeColor}">`,
|
|
114
|
+
`<link rel="icon" type="image/png" sizes="192x192" href="./images/favicon-192.png">`,
|
|
115
|
+
`<link rel="icon" type="image/png" sizes="512x512" href="./images/favicon-512.png">`,
|
|
116
|
+
`<link rel="icon" type="image/x-icon" href="./images/favicon.ico">`,
|
|
117
|
+
`<link rel="apple-touch-icon" href="./images/apple-touch-icon.png">`,
|
|
118
|
+
`<link rel="manifest" href="./manifest.webmanifest">`,
|
|
119
|
+
`<script type="application/ld+json">\n${jsonLd}\n </script>`,
|
|
120
|
+
];
|
|
121
|
+
return lines.map(l => `\n ${l}`).join('');
|
|
122
|
+
}
|
|
52
123
|
/** Build the substitution map. Pure — easy to unit-test. */
|
|
53
124
|
export function buildTemplateVars(v) {
|
|
54
125
|
const safeTitle = escapeHtml(v.projectName);
|
|
@@ -65,6 +136,9 @@ export function buildTemplateVars(v) {
|
|
|
65
136
|
'{{JS_TITLE}}': jsEscape(v.projectName),
|
|
66
137
|
'{{PROJECT_GUID}}': v.projectGuid,
|
|
67
138
|
'{{DATABASE}}': slug,
|
|
139
|
+
'{{HEAD_BLOCK}}': buildHeadBlock(v),
|
|
140
|
+
// Legacy per-tag placeholders — current templates carry only {{HEAD_BLOCK}},
|
|
141
|
+
// but older local template copies may still reference these.
|
|
68
142
|
'{{DESCRIPTION_META}}': v.description ? `\n <meta name="description" content="${safeDesc}">` : '',
|
|
69
143
|
'{{OG_DESCRIPTION}}': v.description ? `\n <meta property="og:description" content="${safeDesc}">` : '',
|
|
70
144
|
'{{JSON_LD_BLOCK}}': `<script type="application/ld+json">\n${jsonLd}\n </script>`,
|
package/dist/utils.js
CHANGED
|
@@ -119,17 +119,28 @@ function rerunWithYes() {
|
|
|
119
119
|
* - `opts.skip` (or the global `--yes` flag) auto-returns `true`.
|
|
120
120
|
* - Renders a `[Y/n]:` or `[y/N]:` hint automatically - callers should NOT
|
|
121
121
|
* append their own y/N suffix (or a trailing `?`/`:`) to `question`.
|
|
122
|
-
* - In non-TTY environments without `--yes`,
|
|
122
|
+
* - In non-TTY environments without `--yes`, this EXITS the process with code 1
|
|
123
|
+
* (it never returns) - see the comment in the body for why. Pass
|
|
124
|
+
* `{ headless: 'no' }` for prompts that merely OFFER something optional,
|
|
125
|
+
* where declining is a normal successful outcome. */
|
|
123
126
|
export async function confirm(question, opts = {}) {
|
|
124
127
|
const defaultYes = opts.default === 'yes';
|
|
125
128
|
if (opts.skip ?? _autoConfirm)
|
|
126
129
|
return true;
|
|
127
130
|
if (!process.stdin.isTTY) {
|
|
128
|
-
// Headless/agent context: no one can answer the prompt.
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
+
// Headless/agent context: no one can answer the prompt. For an optional
|
|
132
|
+
// offer, declining and carrying on is the right answer (`headless: 'no'`).
|
|
133
|
+
// For everything else the command did NOT do what it was asked to do, so
|
|
134
|
+
// exit non-zero: an agent that pipes or suppresses output
|
|
135
|
+
// (`gipity records delete games 1 --purge >/dev/null 2>&1`) otherwise sees
|
|
136
|
+
// a clean exit 0 and believes the delete happened, then burns turns
|
|
137
|
+
// discovering it silently cancelled. The exit status is the only signal
|
|
138
|
+
// that survives redirection. The message echoes the exact command to
|
|
139
|
+
// re-run so the fix is copy-paste, not a second guessing trip.
|
|
140
|
+
if (opts.headless === 'no')
|
|
141
|
+
return false;
|
|
131
142
|
console.error(`Confirmation required (non-interactive). Re-run with --yes:\n ${rerunWithYes()}`);
|
|
132
|
-
|
|
143
|
+
process.exit(1);
|
|
133
144
|
}
|
|
134
145
|
const hint = defaultYes ? dim('[Y/n]') : dim('[y/N]');
|
|
135
146
|
process.stdout.write(`${question} ${hint}: `);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gipity",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.7",
|
|
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",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"prepack": "npm run build",
|
|
15
15
|
"dev": "tsc --watch",
|
|
16
16
|
"test": "npm run test:smoke",
|
|
17
|
-
"test:smoke": "npm run build && 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-unretrievable.test.js dist/__tests__/sync-clean-check.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__/build-picker.test.js dist/__tests__/relay-state.test.js dist/__tests__/relay-daemon.test.js dist/__tests__/session-pool.test.js dist/__tests__/relay-diagnostics.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__/ingest-queue.test.js dist/__tests__/media-upload.test.js dist/__tests__/stream-delta.test.js dist/__tests__/phase-tracker.test.js dist/__tests__/relay-ingest-contract.test.js dist/__tests__/prompts.test.js dist/__tests__/capture-transcript.test.js dist/__tests__/capture-parsers.test.js dist/__tests__/agents.test.js dist/__tests__/capture-lock.test.js dist/__tests__/capture-resolve.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-bug.test.js dist/__tests__/cli-cmd-bug-queue.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-storage.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-status.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-setup.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 dist/__tests__/setup-codex-hooks.test.js dist/__tests__/trace.test.js",
|
|
17
|
+
"test:smoke": "npm run build && node --test dist/__tests__/utils.test.js dist/__tests__/template-vars.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-unretrievable.test.js dist/__tests__/sync-clean-check.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__/build-picker.test.js dist/__tests__/relay-state.test.js dist/__tests__/relay-daemon.test.js dist/__tests__/session-pool.test.js dist/__tests__/relay-diagnostics.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__/ingest-queue.test.js dist/__tests__/media-upload.test.js dist/__tests__/stream-delta.test.js dist/__tests__/phase-tracker.test.js dist/__tests__/relay-ingest-contract.test.js dist/__tests__/prompts.test.js dist/__tests__/capture-transcript.test.js dist/__tests__/capture-parsers.test.js dist/__tests__/agents.test.js dist/__tests__/capture-lock.test.js dist/__tests__/capture-resolve.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-brand.test.js dist/__tests__/cli-cmd-bug.test.js dist/__tests__/cli-cmd-bug-queue.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-db-checkpoint.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-storage.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-status.test.js dist/__tests__/cli-cmd-logout.test.js dist/__tests__/cli-cmd-token.test.js dist/__tests__/cli-cmd-key.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-setup.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 dist/__tests__/setup-codex-hooks.test.js dist/__tests__/trace.test.js",
|
|
18
18
|
"test:smoke:quick": "node scripts/smoke-quick.mjs",
|
|
19
19
|
"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-sync-stress-live.test.js dist/__tests__/cli-e2e-rollback-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",
|
|
20
20
|
"test:e2e:sync": "tsc && GIPITY_E2E=1 node --test --test-timeout=180000 dist/__tests__/cli-e2e-sync-live.test.js",
|