studiograph 1.1.2 → 1.2.0-beta.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/LICENSE +191 -0
- package/README.md +301 -10
- package/dist/agent/orchestrator.d.ts +17 -9
- package/dist/agent/orchestrator.js +142 -97
- package/dist/agent/orchestrator.js.map +1 -1
- package/dist/agent/prompts/system.md +186 -0
- package/dist/agent/skill-loader.d.ts +48 -0
- package/dist/agent/skill-loader.js +166 -0
- package/dist/agent/skill-loader.js.map +1 -0
- package/dist/agent/skills/enrich-entities.md +136 -0
- package/dist/agent/skills/entity-schema.md +502 -0
- package/dist/agent/skills/gather-context.md +46 -0
- package/dist/agent/skills/obsidian-source-setup.md +246 -0
- package/dist/agent/skills/skill-loader.d.ts +48 -0
- package/dist/agent/skills/skill-loader.js +166 -0
- package/dist/agent/skills/skill-loader.js.map +1 -0
- package/dist/agent/skills/sync-configuration.md +144 -0
- package/dist/agent/skills/sync-setup.md +68 -0
- package/dist/agent/tools/connector-tools.d.ts +37 -0
- package/dist/agent/tools/connector-tools.js +132 -0
- package/dist/agent/tools/connector-tools.js.map +1 -0
- package/dist/agent/tools/fs-tools.d.ts +39 -0
- package/dist/agent/tools/fs-tools.js +106 -0
- package/dist/agent/tools/fs-tools.js.map +1 -0
- package/dist/agent/tools/graph-tools.d.ts +30 -2
- package/dist/agent/tools/graph-tools.js +154 -37
- package/dist/agent/tools/graph-tools.js.map +1 -1
- package/dist/agent/tools/load-skill.d.ts +42 -0
- package/dist/agent/tools/load-skill.js +45 -0
- package/dist/agent/tools/load-skill.js.map +1 -0
- package/dist/agent/tools/sync-tools.d.ts +25 -0
- package/dist/agent/tools/sync-tools.js +691 -0
- package/dist/agent/tools/sync-tools.js.map +1 -0
- package/dist/agent/tools/tool-loader.d.ts +25 -0
- package/dist/agent/tools/tool-loader.js +73 -0
- package/dist/agent/tools/tool-loader.js.map +1 -0
- package/dist/auth/github.d.ts +11 -8
- package/dist/auth/github.js +56 -75
- package/dist/auth/github.js.map +1 -1
- package/dist/cli/colors.d.ts +54 -0
- package/dist/cli/colors.js +133 -0
- package/dist/cli/colors.js.map +1 -0
- package/dist/cli/commands/app.d.ts +7 -0
- package/dist/cli/commands/app.js +167 -0
- package/dist/cli/commands/app.js.map +1 -0
- package/dist/cli/commands/auth.d.ts +1 -1
- package/dist/cli/commands/auth.js +26 -10
- package/dist/cli/commands/auth.js.map +1 -1
- package/dist/cli/commands/clone.d.ts +9 -0
- package/dist/cli/commands/clone.js +167 -0
- package/dist/cli/commands/clone.js.map +1 -0
- package/dist/cli/commands/commit.d.ts +8 -0
- package/dist/cli/commands/commit.js +43 -0
- package/dist/cli/commands/commit.js.map +1 -0
- package/dist/cli/commands/config.d.ts +13 -0
- package/dist/cli/commands/config.js +276 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/connector.d.ts +33 -0
- package/dist/cli/commands/connector.js +178 -0
- package/dist/cli/commands/connector.js.map +1 -0
- package/dist/cli/commands/deploy.d.ts +11 -0
- package/dist/cli/commands/deploy.js +153 -0
- package/dist/cli/commands/deploy.js.map +1 -0
- package/dist/cli/commands/enrich.d.ts +11 -0
- package/dist/cli/commands/enrich.js +135 -0
- package/dist/cli/commands/enrich.js.map +1 -0
- package/dist/cli/commands/graphrag.d.ts +12 -0
- package/dist/cli/commands/graphrag.js +122 -0
- package/dist/cli/commands/graphrag.js.map +1 -0
- package/dist/cli/commands/index.d.ts +15 -0
- package/dist/cli/commands/index.js +117 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/init.js +110 -210
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/join.js +89 -24
- package/dist/cli/commands/join.js.map +1 -1
- package/dist/cli/commands/lint.d.ts +8 -0
- package/dist/cli/commands/lint.js +70 -0
- package/dist/cli/commands/lint.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +27 -0
- package/dist/cli/commands/mcp.js +56 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/orphans.d.ts +8 -0
- package/dist/cli/commands/orphans.js +125 -0
- package/dist/cli/commands/orphans.js.map +1 -0
- package/dist/cli/commands/provision.d.ts +8 -0
- package/dist/cli/commands/provision.js +116 -0
- package/dist/cli/commands/provision.js.map +1 -0
- package/dist/cli/commands/r2.d.ts +2 -0
- package/dist/cli/commands/r2.js +87 -6
- package/dist/cli/commands/r2.js.map +1 -1
- package/dist/cli/commands/reset.d.ts +12 -0
- package/dist/cli/commands/reset.js +137 -0
- package/dist/cli/commands/reset.js.map +1 -0
- package/dist/cli/commands/review.d.ts +19 -0
- package/dist/cli/commands/review.js +128 -0
- package/dist/cli/commands/review.js.map +1 -0
- package/dist/cli/commands/serve.js +47 -2
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/source.d.ts +16 -0
- package/dist/cli/commands/source.js +159 -0
- package/dist/cli/commands/source.js.map +1 -0
- package/dist/cli/commands/start.js +472 -103
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/sync-entities.d.ts +13 -0
- package/dist/cli/commands/sync-entities.js +242 -0
- package/dist/cli/commands/sync-entities.js.map +1 -0
- package/dist/cli/commands/sync.js +40 -9
- package/dist/cli/commands/sync.js.map +1 -1
- package/dist/cli/commands/update.d.ts +8 -0
- package/dist/cli/commands/update.js +155 -0
- package/dist/cli/commands/update.js.map +1 -0
- package/dist/cli/index.js +114 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/scaffolding.d.ts +10 -0
- package/dist/cli/scaffolding.js +302 -0
- package/dist/cli/scaffolding.js.map +1 -0
- package/dist/cli/setup-wizard.d.ts +30 -0
- package/dist/cli/setup-wizard.js +244 -0
- package/dist/cli/setup-wizard.js.map +1 -0
- package/dist/cli/sync-review-interactive.d.ts +31 -0
- package/dist/cli/sync-review-interactive.js +393 -0
- package/dist/cli/sync-review-interactive.js.map +1 -0
- package/dist/cli/theme.d.ts +31 -0
- package/dist/cli/theme.js +116 -0
- package/dist/cli/theme.js.map +1 -0
- package/dist/core/graph.d.ts +16 -9
- package/dist/core/graph.js +263 -145
- package/dist/core/graph.js.map +1 -1
- package/dist/core/migration-runner.d.ts +42 -0
- package/dist/core/migration-runner.js +232 -0
- package/dist/core/migration-runner.js.map +1 -0
- package/dist/core/migration-types.d.ts +101 -0
- package/dist/core/migration-types.js +21 -0
- package/dist/core/migration-types.js.map +1 -0
- package/dist/core/migrations/20260219-formalize-memory-location.d.ts +2 -0
- package/dist/core/migrations/20260219-formalize-memory-location.js +35 -0
- package/dist/core/migrations/20260219-formalize-memory-location.js.map +1 -0
- package/dist/core/migrations/20260220-add-workspace-metadata.d.ts +12 -0
- package/dist/core/migrations/20260220-add-workspace-metadata.js +65 -0
- package/dist/core/migrations/20260220-add-workspace-metadata.js.map +1 -0
- package/dist/core/migrations/20260220-add-workspace-readme.d.ts +11 -0
- package/dist/core/migrations/20260220-add-workspace-readme.js +82 -0
- package/dist/core/migrations/20260220-add-workspace-readme.js.map +1 -0
- package/dist/core/migrations/20260220-migrate-yaml-to-json.d.ts +9 -0
- package/dist/core/migrations/20260220-migrate-yaml-to-json.js +64 -0
- package/dist/core/migrations/20260220-migrate-yaml-to-json.js.map +1 -0
- package/dist/core/migrations/index.d.ts +11 -0
- package/dist/core/migrations/index.js +23 -0
- package/dist/core/migrations/index.js.map +1 -0
- package/dist/core/schema-registry.d.ts +36 -0
- package/dist/core/schema-registry.js +161 -0
- package/dist/core/schema-registry.js.map +1 -0
- package/dist/core/types.d.ts +242 -3
- package/dist/core/types.js +21 -2
- package/dist/core/types.js.map +1 -1
- package/dist/core/user-config.d.ts +16 -0
- package/dist/core/user-config.js +8 -0
- package/dist/core/user-config.js.map +1 -1
- package/dist/core/validation.d.ts +973 -32
- package/dist/core/validation.js +163 -4
- package/dist/core/validation.js.map +1 -1
- package/dist/core/workspace-manager.d.ts +26 -2
- package/dist/core/workspace-manager.js +113 -15
- package/dist/core/workspace-manager.js.map +1 -1
- package/dist/core/workspace.d.ts +20 -11
- package/dist/core/workspace.js +123 -34
- package/dist/core/workspace.js.map +1 -1
- package/dist/mcp/connector-manager.d.ts +65 -0
- package/dist/mcp/connector-manager.js +223 -0
- package/dist/mcp/connector-manager.js.map +1 -0
- package/dist/mcp/connectors/asana.d.ts +2 -0
- package/dist/mcp/connectors/asana.js +20 -0
- package/dist/mcp/connectors/asana.js.map +1 -0
- package/dist/mcp/connectors/definitions.d.ts +45 -0
- package/dist/mcp/connectors/definitions.js +32 -0
- package/dist/mcp/connectors/definitions.js.map +1 -0
- package/dist/mcp/connectors/figma.d.ts +5 -0
- package/dist/mcp/connectors/figma.js +21 -0
- package/dist/mcp/connectors/figma.js.map +1 -0
- package/dist/mcp/connectors/gdrive.d.ts +2 -0
- package/dist/mcp/connectors/gdrive.js +20 -0
- package/dist/mcp/connectors/gdrive.js.map +1 -0
- package/dist/mcp/connectors/granola.d.ts +2 -0
- package/dist/mcp/connectors/granola.js +12 -0
- package/dist/mcp/connectors/granola.js.map +1 -0
- package/dist/mcp/connectors/linear.d.ts +2 -0
- package/dist/mcp/connectors/linear.js +19 -0
- package/dist/mcp/connectors/linear.js.map +1 -0
- package/dist/mcp/connectors/obsidian.d.ts +2 -0
- package/dist/mcp/connectors/obsidian.js +19 -0
- package/dist/mcp/connectors/obsidian.js.map +1 -0
- package/dist/mcp/connectors/pipedrive.d.ts +2 -0
- package/dist/mcp/connectors/pipedrive.js +20 -0
- package/dist/mcp/connectors/pipedrive.js.map +1 -0
- package/dist/mcp/connectors/slack.d.ts +2 -0
- package/dist/mcp/connectors/slack.js +21 -0
- package/dist/mcp/connectors/slack.js.map +1 -0
- package/dist/mcp/oauth-provider.d.ts +41 -0
- package/dist/mcp/oauth-provider.js +160 -0
- package/dist/mcp/oauth-provider.js.map +1 -0
- package/dist/mcp/server.d.ts +11 -0
- package/dist/mcp/server.js +28 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +14 -0
- package/dist/mcp/tools.js +172 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/server/index.js +17 -4
- package/dist/server/index.js.map +1 -1
- package/dist/server/plugin-loader.d.ts +15 -0
- package/dist/server/plugin-loader.js +68 -2
- package/dist/server/plugin-loader.js.map +1 -1
- package/dist/server/routes/graph-api.js +1 -1
- package/dist/server/routes/graph-api.js.map +1 -1
- package/dist/server/routes/webhook.js +33 -0
- package/dist/server/routes/webhook.js.map +1 -1
- package/dist/services/github-provisioner.d.ts +9 -3
- package/dist/services/github-provisioner.js +46 -8
- package/dist/services/github-provisioner.js.map +1 -1
- package/dist/services/lint-service.d.ts +27 -0
- package/dist/services/lint-service.js +83 -0
- package/dist/services/lint-service.js.map +1 -0
- package/dist/services/markdown.d.ts +9 -0
- package/dist/services/markdown.js +26 -5
- package/dist/services/markdown.js.map +1 -1
- package/dist/services/memory-service.d.ts +1 -2
- package/dist/services/memory-service.js +5 -4
- package/dist/services/memory-service.js.map +1 -1
- package/dist/services/orphan-service.d.ts +31 -0
- package/dist/services/orphan-service.js +100 -0
- package/dist/services/orphan-service.js.map +1 -0
- package/dist/services/sync/commit.d.ts +58 -0
- package/dist/services/sync/commit.js +350 -0
- package/dist/services/sync/commit.js.map +1 -0
- package/dist/services/sync/context-index.d.ts +69 -0
- package/dist/services/sync/context-index.js +280 -0
- package/dist/services/sync/context-index.js.map +1 -0
- package/dist/services/sync/derive.d.ts +34 -0
- package/dist/services/sync/derive.js +164 -0
- package/dist/services/sync/derive.js.map +1 -0
- package/dist/services/sync/enrichment-state.d.ts +31 -0
- package/dist/services/sync/enrichment-state.js +63 -0
- package/dist/services/sync/enrichment-state.js.map +1 -0
- package/dist/services/sync/enrichment.d.ts +25 -0
- package/dist/services/sync/enrichment.js +121 -0
- package/dist/services/sync/enrichment.js.map +1 -0
- package/dist/services/sync/frontmatter-extractor.d.ts +40 -0
- package/dist/services/sync/frontmatter-extractor.js +273 -0
- package/dist/services/sync/frontmatter-extractor.js.map +1 -0
- package/dist/services/sync/graph-match-state.d.ts +33 -0
- package/dist/services/sync/graph-match-state.js +61 -0
- package/dist/services/sync/graph-match-state.js.map +1 -0
- package/dist/services/sync/graph-match.d.ts +53 -0
- package/dist/services/sync/graph-match.js +316 -0
- package/dist/services/sync/graph-match.js.map +1 -0
- package/dist/services/sync/graphrag-client.d.ts +43 -0
- package/dist/services/sync/graphrag-client.js +94 -0
- package/dist/services/sync/graphrag-client.js.map +1 -0
- package/dist/services/sync/graphrag-config.d.ts +16 -0
- package/dist/services/sync/graphrag-config.js +39 -0
- package/dist/services/sync/graphrag-config.js.map +1 -0
- package/dist/services/sync/graphrag-context.d.ts +14 -0
- package/dist/services/sync/graphrag-context.js +109 -0
- package/dist/services/sync/graphrag-context.js.map +1 -0
- package/dist/services/sync/graphrag-indexer.d.ts +30 -0
- package/dist/services/sync/graphrag-indexer.js +358 -0
- package/dist/services/sync/graphrag-indexer.js.map +1 -0
- package/dist/services/sync/llm.d.ts +32 -0
- package/dist/services/sync/llm.js +115 -0
- package/dist/services/sync/llm.js.map +1 -0
- package/dist/services/sync/mcp-client.d.ts +59 -0
- package/dist/services/sync/mcp-client.js +285 -0
- package/dist/services/sync/mcp-client.js.map +1 -0
- package/dist/services/sync/model-factory.d.ts +10 -0
- package/dist/services/sync/model-factory.js +24 -0
- package/dist/services/sync/model-factory.js.map +1 -0
- package/dist/services/sync/name-quality.d.ts +31 -0
- package/dist/services/sync/name-quality.js +60 -0
- package/dist/services/sync/name-quality.js.map +1 -0
- package/dist/services/sync/output-schemas.d.ts +92 -0
- package/dist/services/sync/output-schemas.js +43 -0
- package/dist/services/sync/output-schemas.js.map +1 -0
- package/dist/services/sync/prompts.d.ts +19 -0
- package/dist/services/sync/prompts.js +128 -0
- package/dist/services/sync/prompts.js.map +1 -0
- package/dist/services/sync/reconciler.d.ts +48 -0
- package/dist/services/sync/reconciler.js +295 -0
- package/dist/services/sync/reconciler.js.map +1 -0
- package/dist/services/sync/source-config.d.ts +45 -0
- package/dist/services/sync/source-config.js +208 -0
- package/dist/services/sync/source-config.js.map +1 -0
- package/dist/services/sync/source-definitions/asana.d.ts +15 -0
- package/dist/services/sync/source-definitions/asana.js +48 -0
- package/dist/services/sync/source-definitions/asana.js.map +1 -0
- package/dist/services/sync/source-definitions/definitions.d.ts +21 -0
- package/dist/services/sync/source-definitions/definitions.js +26 -0
- package/dist/services/sync/source-definitions/definitions.js.map +1 -0
- package/dist/services/sync/source-definitions/gdrive.d.ts +16 -0
- package/dist/services/sync/source-definitions/gdrive.js +68 -0
- package/dist/services/sync/source-definitions/gdrive.js.map +1 -0
- package/dist/services/sync/source-definitions/granola.d.ts +2 -0
- package/dist/services/sync/source-definitions/granola.js +28 -0
- package/dist/services/sync/source-definitions/granola.js.map +1 -0
- package/dist/services/sync/source-definitions/linear.d.ts +2 -0
- package/dist/services/sync/source-definitions/linear.js +60 -0
- package/dist/services/sync/source-definitions/linear.js.map +1 -0
- package/dist/services/sync/source-definitions/obsidian.d.ts +2 -0
- package/dist/services/sync/source-definitions/obsidian.js +55 -0
- package/dist/services/sync/source-definitions/obsidian.js.map +1 -0
- package/dist/services/sync/source-definitions/pipedrive.d.ts +2 -0
- package/dist/services/sync/source-definitions/pipedrive.js +52 -0
- package/dist/services/sync/source-definitions/pipedrive.js.map +1 -0
- package/dist/services/sync/staging.d.ts +53 -0
- package/dist/services/sync/staging.js +131 -0
- package/dist/services/sync/staging.js.map +1 -0
- package/dist/services/sync/structured-extractor.d.ts +49 -0
- package/dist/services/sync/structured-extractor.js +344 -0
- package/dist/services/sync/structured-extractor.js.map +1 -0
- package/dist/services/sync/sync-runner.d.ts +32 -0
- package/dist/services/sync/sync-runner.js +195 -0
- package/dist/services/sync/sync-runner.js.map +1 -0
- package/dist/services/sync/sync-state.d.ts +43 -0
- package/dist/services/sync/sync-state.js +154 -0
- package/dist/services/sync/sync-state.js.map +1 -0
- package/dist/services/sync/types.d.ts +203 -0
- package/dist/services/sync/types.js +8 -0
- package/dist/services/sync/types.js.map +1 -0
- package/dist/services/sync/unstructured-extractor.d.ts +29 -0
- package/dist/services/sync/unstructured-extractor.js +197 -0
- package/dist/services/sync/unstructured-extractor.js.map +1 -0
- package/dist/services/vector-service.d.ts +88 -0
- package/dist/services/vector-service.js +322 -0
- package/dist/services/vector-service.js.map +1 -0
- package/dist/utils/git.d.ts +26 -4
- package/dist/utils/git.js +55 -7
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/merge-resolver.d.ts +34 -0
- package/dist/utils/merge-resolver.js +201 -0
- package/dist/utils/merge-resolver.js.map +1 -0
- package/dist/utils/preflight.d.ts +2 -1
- package/dist/utils/preflight.js +8 -1
- package/dist/utils/preflight.js.map +1 -1
- package/dist/utils/version-checker.d.ts +23 -0
- package/dist/utils/version-checker.js +116 -0
- package/dist/utils/version-checker.js.map +1 -0
- package/dist/utils/workspace-config.d.ts +8 -0
- package/dist/utils/workspace-config.js +22 -0
- package/dist/utils/workspace-config.js.map +1 -0
- package/package.json +24 -11
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync & Enrich Tools for Studiograph Agent
|
|
3
|
+
*
|
|
4
|
+
* These tools allow the agent to orchestrate sync and enrich operations:
|
|
5
|
+
* - Run the sync pipeline, apply staging, commit
|
|
6
|
+
* - Run LLM enrichment on entities
|
|
7
|
+
* - Manage source configs
|
|
8
|
+
*/
|
|
9
|
+
import { Type as T } from '@sinclair/typebox';
|
|
10
|
+
import { spawnSync } from 'child_process';
|
|
11
|
+
import { gitPush, gitPull } from '../../utils/git.js';
|
|
12
|
+
import { existsSync } from 'fs';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
import { homedir } from 'os';
|
|
15
|
+
import { runSync } from '../../services/sync/sync-runner.js';
|
|
16
|
+
import { commitStaging, applyStaging, commitRepos } from '../../services/sync/commit.js';
|
|
17
|
+
import { runEnrichment } from '../../services/sync/enrichment.js';
|
|
18
|
+
import { StagingManager } from '../../services/sync/staging.js';
|
|
19
|
+
import { SourceConfigManager } from '../../services/sync/source-config.js';
|
|
20
|
+
import { ConnectorManager } from '../../mcp/connector-manager.js';
|
|
21
|
+
import { SOURCE_DEFINITIONS } from '../../services/sync/source-definitions/definitions.js';
|
|
22
|
+
import { wrapToolResult } from './graph-tools.js';
|
|
23
|
+
import { VectorService } from '../../services/vector-service.js';
|
|
24
|
+
import { BaseGraphManager } from '../../core/graph.js';
|
|
25
|
+
import { loadUserConfig } from '../../core/user-config.js';
|
|
26
|
+
/**
|
|
27
|
+
* Create sync tools for the agent
|
|
28
|
+
*/
|
|
29
|
+
export function createSyncTools(workspacePath, workspaceConfig) {
|
|
30
|
+
return [
|
|
31
|
+
// Run sync pipeline
|
|
32
|
+
{
|
|
33
|
+
name: 'sync_run',
|
|
34
|
+
label: 'Run Sync',
|
|
35
|
+
description: 'Run the sync pipeline to extract data from external sources into staging. Review with sync_status before committing.',
|
|
36
|
+
parameters: T.Object({
|
|
37
|
+
sources: T.Optional(T.Array(T.String(), { description: 'Source names to sync (default: all enabled)' })),
|
|
38
|
+
dryRun: T.Optional(T.Boolean({ description: 'Preview without writing staging files' })),
|
|
39
|
+
incremental: T.Optional(T.Boolean({ description: 'Only sync records updated since last sync' })),
|
|
40
|
+
}),
|
|
41
|
+
execute: async (toolCallId, params) => {
|
|
42
|
+
try {
|
|
43
|
+
const progress = [];
|
|
44
|
+
const result = await runSync({
|
|
45
|
+
workspacePath,
|
|
46
|
+
sources: params.sources,
|
|
47
|
+
dryRun: params.dryRun,
|
|
48
|
+
incremental: params.incremental,
|
|
49
|
+
schemaExtensions: workspaceConfig.schema_extensions,
|
|
50
|
+
}, (msg) => progress.push(msg));
|
|
51
|
+
return wrapToolResult({
|
|
52
|
+
success: true,
|
|
53
|
+
result,
|
|
54
|
+
progress: progress.join('\n'),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
return wrapToolResult({
|
|
59
|
+
success: false,
|
|
60
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
// Check staging status
|
|
66
|
+
{
|
|
67
|
+
name: 'sync_status',
|
|
68
|
+
label: 'Sync Status',
|
|
69
|
+
description: 'Check what is currently in staging (pending review). Shows counts of creates, updates, and skips by entity type.',
|
|
70
|
+
parameters: T.Object({}),
|
|
71
|
+
execute: async (toolCallId, params) => {
|
|
72
|
+
try {
|
|
73
|
+
const staging = new StagingManager(workspacePath);
|
|
74
|
+
const summary = staging.getSummary();
|
|
75
|
+
return wrapToolResult({
|
|
76
|
+
success: true,
|
|
77
|
+
hasStaging: summary.total > 0,
|
|
78
|
+
summary,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
return wrapToolResult({
|
|
83
|
+
success: false,
|
|
84
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
// Apply staging to entity files (no git)
|
|
90
|
+
{
|
|
91
|
+
name: 'sync_apply',
|
|
92
|
+
label: 'Apply Staging',
|
|
93
|
+
description: 'Apply staging files to the knowledge graph (writes entity files, does not git commit). Always confirm with the user before calling this.',
|
|
94
|
+
parameters: T.Object({
|
|
95
|
+
force: T.Optional(T.Boolean({ description: 'Force apply even with uncommitted changes in repos' })),
|
|
96
|
+
}),
|
|
97
|
+
execute: async (toolCallId, params) => {
|
|
98
|
+
try {
|
|
99
|
+
const progress = [];
|
|
100
|
+
const result = await applyStaging(workspacePath, (msg) => progress.push(msg), { force: params.force });
|
|
101
|
+
return wrapToolResult({
|
|
102
|
+
success: true,
|
|
103
|
+
result: { created: result.created, updated: result.updated, skipped: result.skipped },
|
|
104
|
+
progress: progress.join('\n'),
|
|
105
|
+
nextSteps: 'Review changes with workspace_review, then commit with workspace_commit or studiograph commit',
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
return wrapToolResult({
|
|
110
|
+
success: false,
|
|
111
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
// Git commit across workspace repos
|
|
117
|
+
{
|
|
118
|
+
name: 'workspace_commit',
|
|
119
|
+
label: 'Commit Changes',
|
|
120
|
+
description: 'Git commit entity changes across all workspace repos. Works after sync_apply, enrich_run, or manual edits. Always confirm with the user before calling this.',
|
|
121
|
+
parameters: T.Object({
|
|
122
|
+
messagePrefix: T.Optional(T.String({ description: 'Commit message prefix (default: "update")' })),
|
|
123
|
+
}),
|
|
124
|
+
execute: async (toolCallId, params) => {
|
|
125
|
+
try {
|
|
126
|
+
const progress = [];
|
|
127
|
+
await commitRepos(workspacePath, (msg) => progress.push(msg), { messagePrefix: params.messagePrefix ?? 'update' });
|
|
128
|
+
return wrapToolResult({
|
|
129
|
+
success: true,
|
|
130
|
+
progress: progress.join('\n'),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
return wrapToolResult({
|
|
135
|
+
success: false,
|
|
136
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
// Commit staging to graph (deprecated — kept for backwards compatibility)
|
|
142
|
+
{
|
|
143
|
+
name: 'sync_commit',
|
|
144
|
+
label: 'Commit Staging (deprecated)',
|
|
145
|
+
description: 'DEPRECATED: Use sync_apply then workspace_commit instead. Commits staging files and auto git-commits.',
|
|
146
|
+
parameters: T.Object({
|
|
147
|
+
force: T.Optional(T.Boolean({ description: 'Force commit even with uncommitted changes in repos' })),
|
|
148
|
+
}),
|
|
149
|
+
execute: async (toolCallId, params) => {
|
|
150
|
+
try {
|
|
151
|
+
const progress = [];
|
|
152
|
+
progress.push('Note: sync_commit is deprecated. Use sync_apply then workspace_commit instead.');
|
|
153
|
+
const result = await commitStaging(workspacePath, (msg) => progress.push(msg), { force: params.force, noGit: false });
|
|
154
|
+
return wrapToolResult({
|
|
155
|
+
success: true,
|
|
156
|
+
result,
|
|
157
|
+
progress: progress.join('\n'),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
return wrapToolResult({
|
|
162
|
+
success: false,
|
|
163
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
// Abort staging
|
|
169
|
+
{
|
|
170
|
+
name: 'sync_abort',
|
|
171
|
+
label: 'Abort Staging',
|
|
172
|
+
description: 'Discard all staging files without committing.',
|
|
173
|
+
parameters: T.Object({}),
|
|
174
|
+
execute: async (toolCallId, params) => {
|
|
175
|
+
try {
|
|
176
|
+
const staging = new StagingManager(workspacePath);
|
|
177
|
+
const hadStaging = staging.hasStaging();
|
|
178
|
+
staging.clear();
|
|
179
|
+
return wrapToolResult({
|
|
180
|
+
success: true,
|
|
181
|
+
hadStaging,
|
|
182
|
+
message: hadStaging ? 'Staging cleared.' : 'No staging files to clear.',
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
return wrapToolResult({
|
|
187
|
+
success: false,
|
|
188
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
// List sources and connectors
|
|
194
|
+
{
|
|
195
|
+
name: 'sync_list_sources',
|
|
196
|
+
label: 'List Sync Sources',
|
|
197
|
+
description: 'Read sync source configurations. Returns full source configs with entity_mappings, field maps, transforms, and all settings. Also lists available connectors and known source definitions. Use this whenever users ask about their source config.',
|
|
198
|
+
parameters: T.Object({}),
|
|
199
|
+
execute: async (toolCallId, params) => {
|
|
200
|
+
try {
|
|
201
|
+
const configMgr = new SourceConfigManager(workspacePath);
|
|
202
|
+
const sources = configMgr.getAll();
|
|
203
|
+
// Load connectors but strip sensitive fields
|
|
204
|
+
const connectors = ConnectorManager.loadConfigs().map(c => ({
|
|
205
|
+
name: c.name,
|
|
206
|
+
type: c.type,
|
|
207
|
+
description: c.description,
|
|
208
|
+
}));
|
|
209
|
+
const availableDefinitions = Object.keys(SOURCE_DEFINITIONS);
|
|
210
|
+
return wrapToolResult({
|
|
211
|
+
success: true,
|
|
212
|
+
sources,
|
|
213
|
+
connectors,
|
|
214
|
+
availableDefinitions,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
return wrapToolResult({
|
|
219
|
+
success: false,
|
|
220
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
// Set up a new source
|
|
226
|
+
{
|
|
227
|
+
name: 'sync_setup_source',
|
|
228
|
+
label: 'Setup Sync Source',
|
|
229
|
+
description: 'Add or reconfigure a sync source. Uses default mappings from source definitions when available.',
|
|
230
|
+
parameters: T.Object({
|
|
231
|
+
name: T.String({ description: 'Source name (lowercase, alphanumeric, hyphens/underscores)' }),
|
|
232
|
+
connector: T.Optional(T.String({ description: 'Connector name (defaults to source name)' })),
|
|
233
|
+
entity_mappings: T.Optional(T.Array(T.Object({}, { additionalProperties: true }), { description: 'Entity mappings (overrides defaults from source definition)' })),
|
|
234
|
+
force: T.Optional(T.Boolean({ description: 'Overwrite existing source config' })),
|
|
235
|
+
}),
|
|
236
|
+
execute: async (toolCallId, params) => {
|
|
237
|
+
try {
|
|
238
|
+
const configMgr = new SourceConfigManager(workspacePath);
|
|
239
|
+
const connectorName = params.connector ?? params.name;
|
|
240
|
+
// Check if source already exists
|
|
241
|
+
const existing = configMgr.get(params.name);
|
|
242
|
+
if (existing && !params.force) {
|
|
243
|
+
return wrapToolResult({
|
|
244
|
+
success: false,
|
|
245
|
+
error: `Source "${params.name}" already exists. Use force: true to overwrite.`,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
// Verify connector exists
|
|
249
|
+
const connectors = ConnectorManager.loadConfigs();
|
|
250
|
+
const connector = connectors.find(c => c.name === connectorName);
|
|
251
|
+
if (!connector) {
|
|
252
|
+
return wrapToolResult({
|
|
253
|
+
success: false,
|
|
254
|
+
error: `Connector "${connectorName}" not found. Set up the connector first using the CLI: studiograph connector add ${connectorName}`,
|
|
255
|
+
availableConnectors: connectors.map(c => c.name),
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
// Build source config
|
|
259
|
+
const definition = SOURCE_DEFINITIONS[params.name] ?? SOURCE_DEFINITIONS[connectorName];
|
|
260
|
+
const mappings = params.entity_mappings ?? definition?.defaultMappings ?? [];
|
|
261
|
+
const config = {
|
|
262
|
+
name: params.name,
|
|
263
|
+
connector: connectorName,
|
|
264
|
+
enabled: true,
|
|
265
|
+
added_at: new Date().toISOString(),
|
|
266
|
+
entity_mappings: mappings,
|
|
267
|
+
};
|
|
268
|
+
// Validate
|
|
269
|
+
const validation = configMgr.validate(config);
|
|
270
|
+
if (!validation.valid) {
|
|
271
|
+
return wrapToolResult({
|
|
272
|
+
success: false,
|
|
273
|
+
error: 'Invalid source config',
|
|
274
|
+
validationErrors: validation.errors,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
configMgr.save(config);
|
|
278
|
+
return wrapToolResult({
|
|
279
|
+
success: true,
|
|
280
|
+
message: `Source "${params.name}" configured with ${mappings.length} entity mapping(s).`,
|
|
281
|
+
config,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
return wrapToolResult({
|
|
286
|
+
success: false,
|
|
287
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
// Remove a source
|
|
293
|
+
{
|
|
294
|
+
name: 'sync_remove_source',
|
|
295
|
+
label: 'Remove Sync Source',
|
|
296
|
+
description: 'Remove a sync source configuration.',
|
|
297
|
+
parameters: T.Object({
|
|
298
|
+
name: T.String({ description: 'Source name to remove' }),
|
|
299
|
+
}),
|
|
300
|
+
execute: async (toolCallId, params) => {
|
|
301
|
+
try {
|
|
302
|
+
const configMgr = new SourceConfigManager(workspacePath);
|
|
303
|
+
const removed = configMgr.remove(params.name);
|
|
304
|
+
return wrapToolResult({
|
|
305
|
+
success: true,
|
|
306
|
+
removed,
|
|
307
|
+
message: removed
|
|
308
|
+
? `Source "${params.name}" removed.`
|
|
309
|
+
: `Source "${params.name}" not found.`,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
return wrapToolResult({
|
|
314
|
+
success: false,
|
|
315
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
// ── Enrich tools ──────────────────────────────────────────────────────────
|
|
321
|
+
// Run enrichment
|
|
322
|
+
{
|
|
323
|
+
name: 'enrich_run',
|
|
324
|
+
label: 'Enrich Entities',
|
|
325
|
+
description: 'Enrich entities with cross-source context via LLM. Writes changes in-place (no staging). Review with workspace_review, then commit with workspace_commit. Always confirm with the user before calling this.',
|
|
326
|
+
parameters: T.Object({
|
|
327
|
+
entityTypes: T.Optional(T.Array(T.String(), { description: 'Only enrich specific entity types' })),
|
|
328
|
+
dryRun: T.Optional(T.Boolean({ description: 'Preview without writing changes' })),
|
|
329
|
+
minRefs: T.Optional(T.Number({ description: 'Minimum context references required (default: 2)' })),
|
|
330
|
+
maxCompleteness: T.Optional(T.Number({ description: 'Maximum completeness to qualify (default: 0.6)' })),
|
|
331
|
+
}),
|
|
332
|
+
execute: async (toolCallId, params) => {
|
|
333
|
+
try {
|
|
334
|
+
const progress = [];
|
|
335
|
+
const result = await runEnrichment({
|
|
336
|
+
workspacePath,
|
|
337
|
+
entityTypes: params.entityTypes,
|
|
338
|
+
dryRun: params.dryRun,
|
|
339
|
+
minRefs: params.minRefs,
|
|
340
|
+
maxCompleteness: params.maxCompleteness,
|
|
341
|
+
schemaExtensions: workspaceConfig.schema_extensions,
|
|
342
|
+
}, (msg) => progress.push(msg));
|
|
343
|
+
return wrapToolResult({
|
|
344
|
+
success: true,
|
|
345
|
+
result,
|
|
346
|
+
progress: progress.join('\n'),
|
|
347
|
+
nextSteps: result.enriched > 0 && !params.dryRun
|
|
348
|
+
? 'Review changes with workspace_review, then commit with workspace_commit or studiograph commit'
|
|
349
|
+
: undefined,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
return wrapToolResult({
|
|
354
|
+
success: false,
|
|
355
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
// Abort enrichment (revert uncommitted changes)
|
|
361
|
+
{
|
|
362
|
+
name: 'enrich_abort',
|
|
363
|
+
label: 'Abort Enrichment',
|
|
364
|
+
description: 'Discard uncommitted enrichment changes by reverting all entity files (git checkout + git clean).',
|
|
365
|
+
parameters: T.Object({}),
|
|
366
|
+
execute: async (toolCallId, _params) => {
|
|
367
|
+
try {
|
|
368
|
+
const repos = workspaceConfig.repos ?? [];
|
|
369
|
+
const reverted = [];
|
|
370
|
+
for (const repo of repos) {
|
|
371
|
+
const repoPath = join(workspacePath, repo.path);
|
|
372
|
+
if (!existsSync(repoPath))
|
|
373
|
+
continue;
|
|
374
|
+
spawnSync('git', ['checkout', '--', '.'], { cwd: repoPath, encoding: 'utf-8' });
|
|
375
|
+
spawnSync('git', ['clean', '-fd'], { cwd: repoPath, encoding: 'utf-8' });
|
|
376
|
+
reverted.push(repo.name);
|
|
377
|
+
}
|
|
378
|
+
return wrapToolResult({
|
|
379
|
+
success: true,
|
|
380
|
+
reverted,
|
|
381
|
+
message: reverted.length > 0
|
|
382
|
+
? `Reverted changes in: ${reverted.join(', ')}`
|
|
383
|
+
: 'No changes to revert.',
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
return wrapToolResult({
|
|
388
|
+
success: false,
|
|
389
|
+
error: String(error instanceof Error ? error.message : error),
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
// Reset workspace (discard uncommitted changes)
|
|
395
|
+
{
|
|
396
|
+
name: 'workspace_reset',
|
|
397
|
+
label: 'Reset Workspace',
|
|
398
|
+
description: 'Discard ALL uncommitted changes across workspace repos: reverts modified files (git checkout) and removes untracked files (git clean -fd). Also clears staging if present. This is destructive — always confirm with the user before calling.',
|
|
399
|
+
parameters: T.Object({}),
|
|
400
|
+
execute: async (toolCallId, _params) => {
|
|
401
|
+
try {
|
|
402
|
+
const repos = workspaceConfig.repos ?? [];
|
|
403
|
+
const resetRepos = [];
|
|
404
|
+
const details = [];
|
|
405
|
+
for (const repo of repos) {
|
|
406
|
+
const repoPath = join(workspacePath, repo.path);
|
|
407
|
+
if (!existsSync(repoPath))
|
|
408
|
+
continue;
|
|
409
|
+
// Check if there are changes to reset
|
|
410
|
+
const status = spawnSync('git', ['status', '--porcelain'], { cwd: repoPath, encoding: 'utf-8' });
|
|
411
|
+
if (status.status !== 0 || !status.stdout.trim())
|
|
412
|
+
continue;
|
|
413
|
+
const fileCount = status.stdout.trim().split('\n').length;
|
|
414
|
+
// Revert tracked file changes
|
|
415
|
+
spawnSync('git', ['checkout', '--', '.'], { cwd: repoPath, encoding: 'utf-8' });
|
|
416
|
+
// Remove untracked files and directories
|
|
417
|
+
spawnSync('git', ['clean', '-fd'], { cwd: repoPath, encoding: 'utf-8' });
|
|
418
|
+
resetRepos.push(repo.name);
|
|
419
|
+
details.push(`${repo.name}: ${fileCount} file(s) reset`);
|
|
420
|
+
}
|
|
421
|
+
// Clear staging too
|
|
422
|
+
let clearedStaging = false;
|
|
423
|
+
try {
|
|
424
|
+
const staging = new StagingManager(workspacePath);
|
|
425
|
+
if (staging.hasStaging()) {
|
|
426
|
+
staging.clear();
|
|
427
|
+
clearedStaging = true;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
catch { /* staging dir may not exist */ }
|
|
431
|
+
const parts = [];
|
|
432
|
+
if (resetRepos.length > 0)
|
|
433
|
+
parts.push(`Reset ${resetRepos.length} repo(s): ${resetRepos.join(', ')}`);
|
|
434
|
+
if (clearedStaging)
|
|
435
|
+
parts.push('Cleared staging');
|
|
436
|
+
return wrapToolResult({
|
|
437
|
+
success: true,
|
|
438
|
+
resetRepos,
|
|
439
|
+
details,
|
|
440
|
+
clearedStaging,
|
|
441
|
+
message: parts.length > 0 ? parts.join('. ') + '.' : 'No changes to reset.',
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
catch (error) {
|
|
445
|
+
return wrapToolResult({
|
|
446
|
+
success: false,
|
|
447
|
+
error: String(error instanceof Error ? error.message : error),
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
// Reindex search
|
|
453
|
+
{
|
|
454
|
+
name: 'workspace_reindex',
|
|
455
|
+
label: 'Reindex Search',
|
|
456
|
+
description: 'Rebuild the search index (LanceDB) for all workspace repos. Run after sync, enrich, or manual entity edits to update search results.',
|
|
457
|
+
parameters: T.Object({
|
|
458
|
+
repo: T.Optional(T.String({ description: 'Reindex a single repo only' })),
|
|
459
|
+
}),
|
|
460
|
+
execute: async (toolCallId, params) => {
|
|
461
|
+
try {
|
|
462
|
+
const userConfig = loadUserConfig();
|
|
463
|
+
const dbPath = userConfig.vector_db_path || join(homedir(), '.studiograph', 'vector');
|
|
464
|
+
const vectorService = new VectorService(dbPath, userConfig.voyage_api_key);
|
|
465
|
+
await vectorService.init();
|
|
466
|
+
const allRepos = workspaceConfig.repos ?? [];
|
|
467
|
+
const repos = params.repo
|
|
468
|
+
? allRepos.filter(r => r.name === params.repo)
|
|
469
|
+
: allRepos;
|
|
470
|
+
if (params.repo && repos.length === 0) {
|
|
471
|
+
return wrapToolResult({
|
|
472
|
+
success: false,
|
|
473
|
+
error: `Repo "${params.repo}" not found in workspace`,
|
|
474
|
+
availableRepos: allRepos.map(r => r.name),
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
const repoResults = {};
|
|
478
|
+
let totalIndexed = 0;
|
|
479
|
+
for (const repo of repos) {
|
|
480
|
+
const repoPath = join(workspacePath, repo.path);
|
|
481
|
+
if (!existsSync(repoPath))
|
|
482
|
+
continue;
|
|
483
|
+
try {
|
|
484
|
+
const graph = new BaseGraphManager({
|
|
485
|
+
repoPath,
|
|
486
|
+
repoName: repo.name,
|
|
487
|
+
gitUser: { id: 'reindex', name: 'reindex', email: 'reindex@studiograph.local' },
|
|
488
|
+
});
|
|
489
|
+
const entities = graph.list();
|
|
490
|
+
if (entities.length > 0) {
|
|
491
|
+
await vectorService.upsertMany(repo.name, entities);
|
|
492
|
+
}
|
|
493
|
+
vectorService.setLastIndexedTime(repo.name);
|
|
494
|
+
repoResults[repo.name] = entities.length;
|
|
495
|
+
totalIndexed += entities.length;
|
|
496
|
+
}
|
|
497
|
+
catch (err) {
|
|
498
|
+
repoResults[repo.name] = -1; // signal failure
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return wrapToolResult({
|
|
502
|
+
success: true,
|
|
503
|
+
indexed: totalIndexed,
|
|
504
|
+
repos: repoResults,
|
|
505
|
+
message: `Indexed ${totalIndexed} entities across ${repos.length} repo(s)`,
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
catch (error) {
|
|
509
|
+
return wrapToolResult({
|
|
510
|
+
success: false,
|
|
511
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
},
|
|
516
|
+
// ── Graph status & review ─────────────────────────────────────────────────
|
|
517
|
+
// Show changed files across repos
|
|
518
|
+
{
|
|
519
|
+
name: 'workspace_status',
|
|
520
|
+
label: 'Workspace Status',
|
|
521
|
+
description: 'Show uncommitted git changes across all workspace repos. Use this when users ask about pending changes, what files are modified, or whether there are changes ready to commit.',
|
|
522
|
+
parameters: T.Object({}),
|
|
523
|
+
execute: async (toolCallId, params) => {
|
|
524
|
+
try {
|
|
525
|
+
const repos = workspaceConfig.repos ?? [];
|
|
526
|
+
const changes = {};
|
|
527
|
+
let totalChanged = 0;
|
|
528
|
+
for (const repo of repos) {
|
|
529
|
+
const repoPath = join(workspacePath, repo.path);
|
|
530
|
+
if (!existsSync(repoPath))
|
|
531
|
+
continue;
|
|
532
|
+
const result = spawnSync('git', ['status', '--porcelain'], { cwd: repoPath, encoding: 'utf-8' });
|
|
533
|
+
if (result.status === 0 && result.stdout.trim()) {
|
|
534
|
+
const files = result.stdout.trim().split('\n').filter(Boolean);
|
|
535
|
+
changes[repo.name] = files;
|
|
536
|
+
totalChanged += files.length;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return wrapToolResult({
|
|
540
|
+
success: true,
|
|
541
|
+
totalChanged,
|
|
542
|
+
changes,
|
|
543
|
+
message: totalChanged > 0
|
|
544
|
+
? `${totalChanged} file${totalChanged === 1 ? '' : 's'} changed across ${Object.keys(changes).length} repo${Object.keys(changes).length === 1 ? '' : 's'}`
|
|
545
|
+
: 'No uncommitted changes.',
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
catch (error) {
|
|
549
|
+
return wrapToolResult({
|
|
550
|
+
success: false,
|
|
551
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
// Show diffs for review
|
|
557
|
+
{
|
|
558
|
+
name: 'workspace_review',
|
|
559
|
+
label: 'Review Changes',
|
|
560
|
+
description: 'Show git diffs for uncommitted entity changes across workspace repos. Use this to preview what will be committed. Optionally filter by entity type or specific file path.',
|
|
561
|
+
parameters: T.Object({
|
|
562
|
+
entityType: T.Optional(T.String({ description: 'Filter diff to a specific entity type (e.g. "person", "deal")' })),
|
|
563
|
+
file: T.Optional(T.String({ description: 'Show diff for a specific file path' })),
|
|
564
|
+
committed: T.Optional(T.Boolean({ description: 'Review last commit instead of uncommitted changes' })),
|
|
565
|
+
}),
|
|
566
|
+
execute: async (toolCallId, params) => {
|
|
567
|
+
try {
|
|
568
|
+
const repos = workspaceConfig.repos ?? [];
|
|
569
|
+
const diffArgs = params.committed
|
|
570
|
+
? ['diff', 'HEAD~1..HEAD']
|
|
571
|
+
: ['diff'];
|
|
572
|
+
const diffs = {};
|
|
573
|
+
for (const repo of repos) {
|
|
574
|
+
const repoPath = join(workspacePath, repo.path);
|
|
575
|
+
if (!existsSync(repoPath))
|
|
576
|
+
continue;
|
|
577
|
+
const args = [...diffArgs];
|
|
578
|
+
if (params.file) {
|
|
579
|
+
args.push('--', params.file);
|
|
580
|
+
}
|
|
581
|
+
else if (params.entityType) {
|
|
582
|
+
args.push('--', `${params.entityType}/`);
|
|
583
|
+
}
|
|
584
|
+
const result = spawnSync('git', args, { cwd: repoPath, encoding: 'utf-8' });
|
|
585
|
+
if (result.status === 0 && result.stdout.trim()) {
|
|
586
|
+
// Truncate very large diffs to avoid overwhelming the context
|
|
587
|
+
const diff = result.stdout;
|
|
588
|
+
diffs[repo.name] = diff.length > 10000
|
|
589
|
+
? diff.slice(0, 10000) + '\n\n... (truncated, use --file for specific entities)'
|
|
590
|
+
: diff;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return wrapToolResult({
|
|
594
|
+
success: true,
|
|
595
|
+
hasDiffs: Object.keys(diffs).length > 0,
|
|
596
|
+
diffs,
|
|
597
|
+
message: Object.keys(diffs).length > 0
|
|
598
|
+
? `Diffs from ${Object.keys(diffs).length} repo(s)`
|
|
599
|
+
: 'No changes found.',
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
catch (error) {
|
|
603
|
+
return wrapToolResult({
|
|
604
|
+
success: false,
|
|
605
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
// Push all repos
|
|
611
|
+
{
|
|
612
|
+
name: 'workspace_push',
|
|
613
|
+
label: 'Push Repos',
|
|
614
|
+
description: 'Git push all workspace repos to their remotes. Always confirm with the user before calling this.',
|
|
615
|
+
parameters: T.Object({}),
|
|
616
|
+
execute: async (_toolCallId, _params) => {
|
|
617
|
+
try {
|
|
618
|
+
const repos = workspaceConfig.repos ?? [];
|
|
619
|
+
const results = {};
|
|
620
|
+
for (const repo of repos) {
|
|
621
|
+
const repoPath = join(workspacePath, repo.path);
|
|
622
|
+
if (!existsSync(repoPath))
|
|
623
|
+
continue;
|
|
624
|
+
try {
|
|
625
|
+
results[repo.name] = gitPush(repoPath);
|
|
626
|
+
}
|
|
627
|
+
catch (err) {
|
|
628
|
+
results[repo.name] = `error: ${err instanceof Error ? err.message : String(err)}`;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
const pushed = Object.values(results).filter(r => r === 'pushed').length;
|
|
632
|
+
return wrapToolResult({
|
|
633
|
+
success: true,
|
|
634
|
+
results,
|
|
635
|
+
message: pushed > 0
|
|
636
|
+
? `Pushed ${pushed} repo${pushed === 1 ? '' : 's'}`
|
|
637
|
+
: 'All repos already up to date.',
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
catch (error) {
|
|
641
|
+
return wrapToolResult({
|
|
642
|
+
success: false,
|
|
643
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
},
|
|
647
|
+
},
|
|
648
|
+
// Pull all repos
|
|
649
|
+
{
|
|
650
|
+
name: 'workspace_pull',
|
|
651
|
+
label: 'Pull Repos',
|
|
652
|
+
description: 'Git pull all workspace repos from their remotes. Reports conflicts if any.',
|
|
653
|
+
parameters: T.Object({}),
|
|
654
|
+
execute: async (_toolCallId, _params) => {
|
|
655
|
+
try {
|
|
656
|
+
const repos = workspaceConfig.repos ?? [];
|
|
657
|
+
const results = {};
|
|
658
|
+
for (const repo of repos) {
|
|
659
|
+
const repoPath = join(workspacePath, repo.path);
|
|
660
|
+
if (!existsSync(repoPath))
|
|
661
|
+
continue;
|
|
662
|
+
try {
|
|
663
|
+
results[repo.name] = gitPull(repoPath);
|
|
664
|
+
}
|
|
665
|
+
catch (err) {
|
|
666
|
+
results[repo.name] = `error: ${err instanceof Error ? err.message : String(err)}`;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
const updated = Object.values(results).filter(r => r === 'updated').length;
|
|
670
|
+
const conflicts = Object.values(results).filter(r => r === 'conflict').length;
|
|
671
|
+
return wrapToolResult({
|
|
672
|
+
success: conflicts === 0,
|
|
673
|
+
results,
|
|
674
|
+
message: conflicts > 0
|
|
675
|
+
? `${conflicts} repo${conflicts === 1 ? '' : 's'} with merge conflicts — resolve manually`
|
|
676
|
+
: updated > 0
|
|
677
|
+
? `Pulled updates in ${updated} repo${updated === 1 ? '' : 's'}`
|
|
678
|
+
: 'All repos already up to date.',
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
catch (error) {
|
|
682
|
+
return wrapToolResult({
|
|
683
|
+
success: false,
|
|
684
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
},
|
|
688
|
+
},
|
|
689
|
+
];
|
|
690
|
+
}
|
|
691
|
+
//# sourceMappingURL=sync-tools.js.map
|