gipity 1.0.429 → 1.1.1
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/claude-code.js +53 -0
- package/dist/agents/codex.js +49 -0
- package/dist/agents/grok.js +54 -0
- package/dist/agents/index.js +23 -0
- package/dist/agents/types.js +18 -0
- package/dist/api.js +7 -0
- package/dist/capture/sources/codex.js +216 -0
- package/dist/capture/sources/grok.js +178 -0
- package/dist/client-context.js +2 -0
- package/dist/commands/build.js +1352 -0
- package/dist/commands/chat.js +9 -3
- package/dist/commands/claude.js +4 -13
- package/dist/commands/db.js +12 -5
- package/dist/commands/doctor.js +8 -2
- package/dist/commands/init.js +9 -15
- package/dist/commands/page-eval.js +18 -7
- package/dist/commands/page-inspect.js +7 -3
- package/dist/commands/page-screenshot.js +62 -23
- package/dist/commands/project.js +1 -1
- package/dist/commands/relay-install.js +1 -1
- package/dist/commands/relay.js +2 -2
- package/dist/commands/sandbox.js +62 -16
- package/dist/commands/setup.js +16 -10
- package/dist/commands/uninstall.js +25 -3
- package/dist/hooks/capture-runner.js +127 -38
- package/dist/index.js +1055 -330
- package/dist/knowledge.js +3 -3
- package/dist/prefs.js +50 -0
- package/dist/project-setup.js +2 -10
- package/dist/relay/daemon.js +124 -16
- package/dist/relay/diagnostics.js +4 -2
- package/dist/relay/onboarding.js +2 -2
- package/dist/setup.js +258 -17
- package/package.json +4 -3
package/dist/setup.js
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
* Shared project setup helpers used by both `init` and `claude`.
|
|
3
3
|
*/
|
|
4
4
|
import { resolve, join, dirname } from 'path';
|
|
5
|
-
import { homedir } from 'os';
|
|
6
|
-
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
5
|
+
import { homedir, tmpdir } from 'os';
|
|
6
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, mkdtempSync, rmSync, cpSync, readdirSync } from 'fs';
|
|
7
7
|
import { resolveCommand, spawnSyncCommand } from './platform.js';
|
|
8
8
|
import { SKILLS_CONTENT, BUILD_VS_NON_BUILD_RULE, DEFINITION_OF_DONE } from './knowledge.js';
|
|
9
|
+
import { DEFAULT_API_BASE, resolveApiBase } from './config.js';
|
|
9
10
|
export { SKILLS_CONTENT };
|
|
10
11
|
/** Canonical list of workstation artifacts that are NOT part of the project.
|
|
11
12
|
* Used as the single source of truth for three separate decisions:
|
|
@@ -30,6 +31,7 @@ export { SKILLS_CONTENT };
|
|
|
30
31
|
export const PRIMER_FILES = {
|
|
31
32
|
claude: 'CLAUDE.md',
|
|
32
33
|
codex: 'AGENTS.md',
|
|
34
|
+
grok: 'AGENTS.md', // Grok Build reads the AGENTS.md family (and CLAUDE.md) natively
|
|
33
35
|
aider: 'AGENTS.md', // shares the Codex primer; aider is pointed at it via .aider.conf.yml
|
|
34
36
|
gemini: 'GEMINI.md',
|
|
35
37
|
copilot: '.github/copilot-instructions.md',
|
|
@@ -56,7 +58,7 @@ export const AIDER_CONF_FILE = '.aider.conf.yml';
|
|
|
56
58
|
* `ignore` package in config.ts. */
|
|
57
59
|
export const SCRATCH_IGNORE = ['tmp/', '.tmp/', '*_tmp/', '.gipityscratch/'];
|
|
58
60
|
export const DEFAULT_SYNC_IGNORE = [
|
|
59
|
-
'node_modules', '.git', '.gipity.json', '.gipity/', '.claude/', '.gitignore', AIDER_CONF_FILE,
|
|
61
|
+
'node_modules', '.git', '.gipity.json', '.gipity/', '.claude/', '.codex/', '.gitignore', AIDER_CONF_FILE,
|
|
60
62
|
// Home-directory junk: a project created inside a real home dir (or one that
|
|
61
63
|
// shells out) sweeps in a cache dir + shell dotfiles that are never app
|
|
62
64
|
// files. `.cache/` alone can be gigabytes (it was 2.4 GB on one project),
|
|
@@ -148,7 +150,7 @@ export const LEGACY_MARKETPLACE_REPO = 'GipityAI/claude-plugin';
|
|
|
148
150
|
// an installed plugin when the marketplace advances - only an explicit
|
|
149
151
|
// `plugin install`/`update` does - so this constant is how a CLI upgrade tells
|
|
150
152
|
// ensureGipityPluginInstalled() to refresh a stale user-scope install.
|
|
151
|
-
export const GIPITY_PLUGIN_VERSION = '0.
|
|
153
|
+
export const GIPITY_PLUGIN_VERSION = '0.7.0';
|
|
152
154
|
/** True for hook commands the CLI itself wrote into settings.json in past
|
|
153
155
|
* versions. Matched by signature so migration strips exactly our own
|
|
154
156
|
* entries and never touches user-authored hooks. */
|
|
@@ -280,8 +282,8 @@ export function userScopeInstallState() {
|
|
|
280
282
|
export function userScopePluginCurrent() {
|
|
281
283
|
return userScopeInstallState().current;
|
|
282
284
|
}
|
|
283
|
-
function
|
|
284
|
-
const probe = spawnSyncCommand(process.platform === 'win32' ? 'where' : 'which', [
|
|
285
|
+
export function binaryOnPath(bin) {
|
|
286
|
+
const probe = spawnSyncCommand(process.platform === 'win32' ? 'where' : 'which', [bin], {
|
|
285
287
|
encoding: 'utf-8',
|
|
286
288
|
});
|
|
287
289
|
return probe.status === 0 && !!probe.stdout?.toString().trim();
|
|
@@ -304,7 +306,7 @@ export function ensureGipityPluginInstalled() {
|
|
|
304
306
|
const state = userScopeInstallState();
|
|
305
307
|
if (state.current)
|
|
306
308
|
return;
|
|
307
|
-
if (!
|
|
309
|
+
if (!binaryOnPath('claude'))
|
|
308
310
|
return;
|
|
309
311
|
// Refresh the marketplace clone so install/update resolves the current version.
|
|
310
312
|
// resolveCommand: on Windows `claude` is a .cmd shim that spawn can't launch
|
|
@@ -330,6 +332,209 @@ export function ensureGipityPluginInstalled() {
|
|
|
330
332
|
timeout: 120_000,
|
|
331
333
|
});
|
|
332
334
|
}
|
|
335
|
+
// --- Grok Build (xAI) ------------------------------------------------------
|
|
336
|
+
// Grok Build reads Claude-format plugins natively - skills/, commands/,
|
|
337
|
+
// hooks/hooks.json, and the .claude-plugin/plugin.json manifest - and sets
|
|
338
|
+
// CLAUDE_PLUGIN_ROOT/CLAUDE_PLUGIN_DATA aliases for plugin hooks, so the one
|
|
339
|
+
// GipityAI/skills repo serves both agents. A user-scope install
|
|
340
|
+
// (`grok plugin install <repo> --trust`) is trusted and enabled automatically,
|
|
341
|
+
// which gives Grok sessions the same skills and file-sync hooks as Claude Code.
|
|
342
|
+
/** Parsed install state for the Gipity plugin in Grok Build, read straight
|
|
343
|
+
* from ~/.grok/installed-plugins/registry.json (no subprocess). Mirrors
|
|
344
|
+
* {@link userScopeInstallState} for Claude Code. */
|
|
345
|
+
export function grokInstallState() {
|
|
346
|
+
try {
|
|
347
|
+
const p = join(homedir(), '.grok', 'installed-plugins', 'registry.json');
|
|
348
|
+
const data = JSON.parse(readFileSync(p, 'utf-8'));
|
|
349
|
+
const versions = [];
|
|
350
|
+
for (const repo of Object.values(data?.repos ?? {})) {
|
|
351
|
+
const v = repo?.plugins?.gipity?.version;
|
|
352
|
+
if (typeof v === 'string')
|
|
353
|
+
versions.push(v);
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
exists: versions.length > 0,
|
|
357
|
+
current: versions.some((v) => versionGte(v, GIPITY_PLUGIN_VERSION)),
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
return { exists: false, current: false };
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/** Install (or upgrade) the Gipity plugin in Grok Build. Best-effort and
|
|
365
|
+
* non-fatal, like the Claude Code counterpart: skips instantly when Grok
|
|
366
|
+
* isn't installed or the plugin is already current, so the steady-state cost
|
|
367
|
+
* is one registry.json read. */
|
|
368
|
+
export function ensureGrokPluginInstalled() {
|
|
369
|
+
const state = grokInstallState();
|
|
370
|
+
if (state.current)
|
|
371
|
+
return;
|
|
372
|
+
if (!binaryOnPath('grok'))
|
|
373
|
+
return;
|
|
374
|
+
const grokCmd = resolveCommand('grok');
|
|
375
|
+
// `install` clones the repo; on an existing install `update <name>` is the
|
|
376
|
+
// verb that refetches the source and advances the recorded version.
|
|
377
|
+
const verb = state.exists
|
|
378
|
+
? ['plugin', 'update', 'gipity']
|
|
379
|
+
: ['plugin', 'install', GIPITY_MARKETPLACE_REPO, '--trust'];
|
|
380
|
+
const res = spawnSyncCommand(grokCmd, verb, { stdio: 'ignore', timeout: 120_000 });
|
|
381
|
+
if (!state.exists && res.status === 0) {
|
|
382
|
+
console.log('Installed the Gipity plugin for Grok (skills + file-sync hooks).');
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// --- OpenAI Codex ------------------------------------------------------------
|
|
386
|
+
// Codex has no Claude-plugin compatibility, but it reads the same SKILL.md
|
|
387
|
+
// skill format from the cross-agent `~/.agents/skills` directory (also read by
|
|
388
|
+
// OpenClaw and other agentskills.io adopters), and supports project hooks in
|
|
389
|
+
// `.codex/hooks.json` with Claude-style matcher groups (`Edit|Write` aliases
|
|
390
|
+
// its apply_patch tool). So Codex setup = copy the plugin's skills into
|
|
391
|
+
// ~/.agents/skills, stage its hook scripts under ~/.gipity/agent-hooks, and
|
|
392
|
+
// write a project .codex/hooks.json pointing at them. Codex requires the user
|
|
393
|
+
// to approve non-managed hooks once via /hooks - there is no supported way to
|
|
394
|
+
// pre-trust, so we print a nudge on first write.
|
|
395
|
+
export const AGENTS_SKILLS_DIR = join(homedir(), '.agents', 'skills');
|
|
396
|
+
export const AGENT_HOOKS_DIR = join(homedir(), '.gipity', 'agent-hooks');
|
|
397
|
+
/** Records what ensureAgentSkillsInstalled() put on this machine: the plugin
|
|
398
|
+
* version and the exact skill names copied, so upgrades replace and uninstall
|
|
399
|
+
* removes precisely those. */
|
|
400
|
+
export const AGENT_SKILLS_MANIFEST = join(homedir(), '.gipity', 'agent-skills.json');
|
|
401
|
+
export function agentSkillsState() {
|
|
402
|
+
try {
|
|
403
|
+
const m = JSON.parse(readFileSync(AGENT_SKILLS_MANIFEST, 'utf-8'));
|
|
404
|
+
return {
|
|
405
|
+
current: typeof m?.version === 'string' && versionGte(m.version, GIPITY_PLUGIN_VERSION),
|
|
406
|
+
skills: Array.isArray(m?.skills) ? m.skills : [],
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
return { current: false, skills: [] };
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
/** Materialize the Gipity skills into ~/.agents/skills (the cross-agent skills
|
|
414
|
+
* dir Codex reads) and the plugin's hook scripts into ~/.gipity/agent-hooks.
|
|
415
|
+
* Source of truth is the same GipityAI/skills repo the Claude/Grok plugin
|
|
416
|
+
* installs clone - fetched with a shallow git clone into a temp dir.
|
|
417
|
+
* Best-effort: no git, no network, or a failed clone all leave things as they
|
|
418
|
+
* were; the next init retries. */
|
|
419
|
+
export function ensureAgentSkillsInstalled() {
|
|
420
|
+
if (agentSkillsState().current)
|
|
421
|
+
return;
|
|
422
|
+
if (!binaryOnPath('git'))
|
|
423
|
+
return;
|
|
424
|
+
const tmp = mkdtempSync(join(tmpdir(), 'gipity-skills-'));
|
|
425
|
+
try {
|
|
426
|
+
const clone = spawnSyncCommand(resolveCommand('git'), ['clone', '--depth', '1', `https://github.com/${GIPITY_MARKETPLACE_REPO}.git`, join(tmp, 'repo')], { stdio: 'ignore', timeout: 120_000 });
|
|
427
|
+
if (clone.status !== 0)
|
|
428
|
+
return;
|
|
429
|
+
const repo = join(tmp, 'repo');
|
|
430
|
+
let version = GIPITY_PLUGIN_VERSION;
|
|
431
|
+
try {
|
|
432
|
+
const manifest = JSON.parse(readFileSync(join(repo, '.claude-plugin', 'plugin.json'), 'utf-8'));
|
|
433
|
+
if (typeof manifest?.version === 'string')
|
|
434
|
+
version = manifest.version;
|
|
435
|
+
}
|
|
436
|
+
catch { /* keep the CLI's pinned version */ }
|
|
437
|
+
const skillsSrc = join(repo, 'skills');
|
|
438
|
+
const names = [];
|
|
439
|
+
for (const entry of readdirSync(skillsSrc, { withFileTypes: true })) {
|
|
440
|
+
if (!entry.isDirectory())
|
|
441
|
+
continue;
|
|
442
|
+
if (!existsSync(join(skillsSrc, entry.name, 'SKILL.md')))
|
|
443
|
+
continue;
|
|
444
|
+
mkdirSync(AGENTS_SKILLS_DIR, { recursive: true });
|
|
445
|
+
cpSync(join(skillsSrc, entry.name), join(AGENTS_SKILLS_DIR, entry.name), {
|
|
446
|
+
recursive: true,
|
|
447
|
+
force: true,
|
|
448
|
+
});
|
|
449
|
+
names.push(entry.name);
|
|
450
|
+
}
|
|
451
|
+
mkdirSync(AGENT_HOOKS_DIR, { recursive: true });
|
|
452
|
+
for (const script of readdirSync(join(repo, 'hooks', 'scripts'))) {
|
|
453
|
+
cpSync(join(repo, 'hooks', 'scripts', script), join(AGENT_HOOKS_DIR, script), { force: true });
|
|
454
|
+
}
|
|
455
|
+
writeFileSync(AGENT_SKILLS_MANIFEST, JSON.stringify({ version, skills: names }, null, 2) + '\n');
|
|
456
|
+
console.log(`Installed ${names.length} Gipity skills for Codex (~/.agents/skills).`);
|
|
457
|
+
}
|
|
458
|
+
catch { /* best-effort - never break setup */ }
|
|
459
|
+
finally {
|
|
460
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
/** Pure core of the .codex/hooks.json merge: given the file's current content
|
|
464
|
+
* (`null` when absent), return the new content, or `null` when no change is
|
|
465
|
+
* needed. Adds the Gipity sync hook groups (push on file edits, pull before
|
|
466
|
+
* each prompt) AND the session-capture groups (SessionStart / throttled
|
|
467
|
+
* PostToolUse / Stop → capture.cjs, which mirrors the session into the web
|
|
468
|
+
* CLI; Codex has no SessionEnd or SubagentStop, so those are absent), while
|
|
469
|
+
* preserving any user-authored hooks. Our groups are recognized by their
|
|
470
|
+
* exact command string, so a re-run upgrades older sync-only files by adding
|
|
471
|
+
* just the missing capture entries. Exported for unit testing. */
|
|
472
|
+
export function applyCodexHooks(existing) {
|
|
473
|
+
const launcher = join(AGENT_HOOKS_DIR, 'launch.sh');
|
|
474
|
+
const cmd = (script, ...args) => [`sh "${launcher}" "${join(AGENT_HOOKS_DIR, script)}"`, ...args].join(' ');
|
|
475
|
+
const wanted = [
|
|
476
|
+
{ event: 'PostToolUse', matcher: 'Edit|Write', command: cmd('sync-push.cjs'), timeout: 30 },
|
|
477
|
+
{ event: 'UserPromptSubmit', command: cmd('sync-pull.cjs'), timeout: 300 },
|
|
478
|
+
// Session capture: mirror the Codex session into the Gipity web CLI.
|
|
479
|
+
{ event: 'SessionStart', command: cmd('capture.cjs', 'codex', 'session-start'), timeout: 30 },
|
|
480
|
+
{ event: 'PostToolUse', command: cmd('capture.cjs', 'codex', 'post-tool-use'), timeout: 30 },
|
|
481
|
+
{ event: 'Stop', command: cmd('capture.cjs', 'codex', 'stop'), timeout: 60 },
|
|
482
|
+
];
|
|
483
|
+
let settings = {};
|
|
484
|
+
if (existing !== null) {
|
|
485
|
+
try {
|
|
486
|
+
settings = JSON.parse(existing);
|
|
487
|
+
}
|
|
488
|
+
catch {
|
|
489
|
+
return null; // user file we can't parse - leave it alone
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const hooks = settings.hooks ?? (settings.hooks = {});
|
|
493
|
+
let changed = false;
|
|
494
|
+
for (const w of wanted) {
|
|
495
|
+
const groups = Array.isArray(hooks[w.event]) ? hooks[w.event] : (hooks[w.event] = []);
|
|
496
|
+
const present = groups.some((g) => Array.isArray(g?.hooks) && g.hooks.some((h) => typeof h?.command === 'string' && h.command === w.command));
|
|
497
|
+
if (present)
|
|
498
|
+
continue;
|
|
499
|
+
const group = { hooks: [{ type: 'command', command: w.command, timeout: w.timeout }] };
|
|
500
|
+
if (w.matcher)
|
|
501
|
+
group.matcher = w.matcher;
|
|
502
|
+
groups.push(group);
|
|
503
|
+
changed = true;
|
|
504
|
+
}
|
|
505
|
+
return changed ? JSON.stringify(settings, null, 2) + '\n' : null;
|
|
506
|
+
}
|
|
507
|
+
/** Write the project-level Codex hooks (.codex/hooks.json). POSIX only - the
|
|
508
|
+
* commands run through the same sh launcher the plugin uses; Codex on Windows
|
|
509
|
+
* would need commandWindows variants (not wired yet). */
|
|
510
|
+
export function setupCodexHooks() {
|
|
511
|
+
if (process.platform === 'win32')
|
|
512
|
+
return;
|
|
513
|
+
const cwd = resolve(process.cwd());
|
|
514
|
+
if (cwd === resolve(homedir()))
|
|
515
|
+
return; // never treat $HOME as a project
|
|
516
|
+
const path = join(cwd, '.codex', 'hooks.json');
|
|
517
|
+
const existing = existsSync(path) ? readFileSync(path, 'utf-8') : null;
|
|
518
|
+
const next = applyCodexHooks(existing);
|
|
519
|
+
if (next === null)
|
|
520
|
+
return;
|
|
521
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
522
|
+
writeFileSync(path, next);
|
|
523
|
+
if (existing === null) {
|
|
524
|
+
console.log('Wrote Codex sync + session-capture hooks (.codex/hooks.json) - approve them once with /hooks inside Codex.');
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
console.log('Updated Codex hooks (.codex/hooks.json) - if Codex asks, re-approve them via /hooks.');
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
/** Full Codex integration: user-scope skills + project sync hooks. Gated on
|
|
531
|
+
* the codex binary so machines without Codex get only the AGENTS.md primer. */
|
|
532
|
+
export function setupCodexIntegration() {
|
|
533
|
+
if (!binaryOnPath('codex'))
|
|
534
|
+
return;
|
|
535
|
+
ensureAgentSkillsInstalled();
|
|
536
|
+
setupCodexHooks();
|
|
537
|
+
}
|
|
333
538
|
export function setupClaudeHooks() {
|
|
334
539
|
// All hooks ship in the plugin - enable it at user scope (and clean up any
|
|
335
540
|
// legacy hook blocks in the user-global settings while we're there).
|
|
@@ -374,9 +579,25 @@ export const GIPITY_BLOCK_END = '<!-- END GIPITY INTEGRATION -->';
|
|
|
374
579
|
* them into a generated doc is the wrong layer. The CLI surfaces them where the
|
|
375
580
|
* agent actually looks: `gipity deploy` prints the live URL, `gipity status` and
|
|
376
581
|
* `gipity project info` show the URL + GUID. That keeps them authoritative and
|
|
377
|
-
* avoids stale values frozen into a file.
|
|
378
|
-
|
|
379
|
-
|
|
582
|
+
* avoids stale values frozen into a file.
|
|
583
|
+
*
|
|
584
|
+
* The one environment value that DOES live here is the API base. The baked
|
|
585
|
+
* knowledge text names `https://a.gipity.ai` as the app-services endpoint;
|
|
586
|
+
* when this session runs against a different platform instance (GIPITY_API_BASE
|
|
587
|
+
* / --api-base, e.g. a local dev server), the project only exists there - an
|
|
588
|
+
* agent that copies the public host into app code gets 404s it can't explain.
|
|
589
|
+
* So the block is rendered against the resolved base, with a note naming the
|
|
590
|
+
* instance. The block is fully regenerated each session, so it tracks the
|
|
591
|
+
* environment rather than going stale. */
|
|
592
|
+
function renderManagedBlock(apiBase) {
|
|
593
|
+
let body = [SKILLS_CONTENT, BUILD_VS_NON_BUILD_RULE, DEFINITION_OF_DONE].join('\n\n');
|
|
594
|
+
const base = apiBase.replace(/\/+$/, '');
|
|
595
|
+
if (base !== DEFAULT_API_BASE) {
|
|
596
|
+
body = body.replaceAll(DEFAULT_API_BASE, base);
|
|
597
|
+
const note = `> **Platform instance:** this project runs against the Gipity platform at \`${base}\`, not the public \`${DEFAULT_API_BASE}\`. The project and its data exist only on that instance; every API/service URL in this document already points there - never substitute \`a.gipity.ai\`.`;
|
|
598
|
+
const headingEnd = body.indexOf('\n');
|
|
599
|
+
body = body.slice(0, headingEnd + 1) + '\n' + note + '\n' + body.slice(headingEnd + 1);
|
|
600
|
+
}
|
|
380
601
|
return `${GIPITY_BLOCK_BEGIN}\n${body}\n${GIPITY_BLOCK_END}`;
|
|
381
602
|
}
|
|
382
603
|
/**
|
|
@@ -396,8 +617,8 @@ function renderManagedBlock() {
|
|
|
396
617
|
*
|
|
397
618
|
* Exported for unit testing.
|
|
398
619
|
*/
|
|
399
|
-
export function applySkillsBlock(existing) {
|
|
400
|
-
const block = renderManagedBlock();
|
|
620
|
+
export function applySkillsBlock(existing, apiBase = DEFAULT_API_BASE) {
|
|
621
|
+
const block = renderManagedBlock(apiBase);
|
|
401
622
|
if (existing === null)
|
|
402
623
|
return block + '\n';
|
|
403
624
|
let next;
|
|
@@ -425,7 +646,7 @@ function writeSkillsFile(relPath, wrap) {
|
|
|
425
646
|
const path = resolve(process.cwd(), relPath);
|
|
426
647
|
mkdirSync(dirname(path), { recursive: true });
|
|
427
648
|
const existing = existsSync(path) ? readFileSync(path, 'utf-8') : null;
|
|
428
|
-
const baseNext = applySkillsBlock(existing);
|
|
649
|
+
const baseNext = applySkillsBlock(existing, resolveApiBase());
|
|
429
650
|
// Frontmatter wrap only applies on first write (no existing content).
|
|
430
651
|
// Re-runs preserve user content outside the managed block, including
|
|
431
652
|
// any frontmatter they may have added themselves.
|
|
@@ -509,15 +730,22 @@ export function setupCopilotMd() {
|
|
|
509
730
|
export function setupCursorMd() {
|
|
510
731
|
writeSkillsFile(PRIMER_FILES.cursor, (block) => `---\ndescription: Gipity platform integration - CLI, sandbox, app services\nalwaysApply: true\n---\n\n${block}`);
|
|
511
732
|
}
|
|
512
|
-
/** All supported coding
|
|
513
|
-
*
|
|
733
|
+
/** All supported coding tools: the primer each gets (`setup`) plus, for tools
|
|
734
|
+
* with a deeper Gipity integration, an `integrate` step that installs the
|
|
735
|
+
* Gipity skills and file-sync hooks into that tool's own ecosystem (Claude
|
|
736
|
+
* Code plugin, Grok Build plugin, Codex ~/.agents/skills + .codex hooks).
|
|
737
|
+
* Integrations are best-effort and self-gating - each no-ops fast when its
|
|
738
|
+
* binary is missing or the install is already current - so running the whole
|
|
739
|
+
* default set on every init/launch is cheap and keeps all detected agents in
|
|
740
|
+
* lockstep. Order matters for help-text rendering and the `all` expansion.
|
|
514
741
|
* `optIn` tools are excluded from the default / `all` set and must be named
|
|
515
742
|
* explicitly (`--for aider`): aider's setup writes `.aider.conf.yml`, which
|
|
516
743
|
* changes how aider behaves in this directory - a heavier footprint than
|
|
517
744
|
* dropping an inert markdown primer. */
|
|
518
745
|
export const SUPPORTED_TOOLS = [
|
|
519
|
-
{ key: 'claude', label: 'Claude Code (CLAUDE.md)', setup: setupClaudeMd },
|
|
520
|
-
{ key: 'codex', label: 'OpenAI Codex (AGENTS.md)', setup: setupAgentsMd },
|
|
746
|
+
{ key: 'claude', label: 'Claude Code (CLAUDE.md + Gipity plugin)', setup: setupClaudeMd, integrate: setupClaudeHooks },
|
|
747
|
+
{ key: 'codex', label: 'OpenAI Codex (AGENTS.md + skills + sync hooks)', setup: setupAgentsMd, integrate: setupCodexIntegration },
|
|
748
|
+
{ key: 'grok', label: 'Grok Build (AGENTS.md + Gipity plugin)', setup: setupAgentsMd, integrate: ensureGrokPluginInstalled },
|
|
521
749
|
{ key: 'aider', label: 'Aider (AGENTS.md + .aider.conf.yml)', setup: setupAiderMd, optIn: true },
|
|
522
750
|
{ key: 'gemini', label: 'Gemini CLI (GEMINI.md)', setup: setupGeminiMd },
|
|
523
751
|
{ key: 'copilot', label: 'GitHub Copilot (.github/copilot-instructions.md)', setup: setupCopilotMd },
|
|
@@ -526,6 +754,19 @@ export const SUPPORTED_TOOLS = [
|
|
|
526
754
|
/** The primer set written when the user makes no explicit `--for` choice:
|
|
527
755
|
* every tool except opt-in ones. */
|
|
528
756
|
export const DEFAULT_TOOLS = SUPPORTED_TOOLS.filter(t => !t.optIn);
|
|
757
|
+
/** Registry-driven project setup - the single entry point every "link this
|
|
758
|
+
* directory" path shares (`init`, `project create`, `gipity claude`, the
|
|
759
|
+
* relay daemon). Writes each requested tool's primer, runs its integration
|
|
760
|
+
* (hooks/skills install) when it has one, and refreshes .gitignore. Replaces
|
|
761
|
+
* the setupClaudeHooks/setupClaudeMd/setupAgentsMd/setupGitignore quartet
|
|
762
|
+
* that used to be copy-pasted per call site and silently skipped newer tools. */
|
|
763
|
+
export function setupProjectTools(tools = DEFAULT_TOOLS) {
|
|
764
|
+
for (const t of tools) {
|
|
765
|
+
t.setup();
|
|
766
|
+
t.integrate?.();
|
|
767
|
+
}
|
|
768
|
+
setupGitignore();
|
|
769
|
+
}
|
|
529
770
|
export function setupGitignore() {
|
|
530
771
|
const gitignorePath = resolve(process.cwd(), '.gitignore');
|
|
531
772
|
// Sync already skips the scratch namespaces (DEFAULT_SYNC_IGNORE); ignore them
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gipity",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
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,13 +14,14 @@
|
|
|
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__/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-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-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__/trace.test.js",
|
|
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__/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-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",
|
|
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",
|
|
21
21
|
"test:e2e:sync-stress": "tsc && GIPITY_E2E=1 node --test --test-timeout=180000 dist/__tests__/cli-e2e-sync-stress-live.test.js",
|
|
22
22
|
"test:e2e:rollback": "tsc && GIPITY_E2E=1 node --test --test-timeout=180000 dist/__tests__/cli-e2e-rollback-live.test.js",
|
|
23
|
-
"test:e2e:sandbox": "tsc && GIPITY_E2E=1 node --test --test-timeout=180000 dist/__tests__/cli-e2e-sandbox-live.test.js"
|
|
23
|
+
"test:e2e:sandbox": "tsc && GIPITY_E2E=1 node --test --test-timeout=180000 dist/__tests__/cli-e2e-sandbox-live.test.js",
|
|
24
|
+
"test:e2e:agents": "tsc && GIPITY_E2E=1 node --test --test-timeout=900000 dist/__tests__/cli-e2e-agents-live.test.js"
|
|
24
25
|
},
|
|
25
26
|
"dependencies": {
|
|
26
27
|
"@anthropic-ai/claude-agent-sdk": "^0.3.207",
|