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
|
@@ -9,11 +9,30 @@ import { getModel } from '@mariozechner/pi-ai';
|
|
|
9
9
|
import { Workspace } from '../../core/workspace.js';
|
|
10
10
|
import { WorkspaceManager } from '../../core/workspace-manager.js';
|
|
11
11
|
import { MemoryService } from '../../services/memory-service.js';
|
|
12
|
+
import { SchemaRegistry } from '../../core/schema-registry.js';
|
|
12
13
|
import { createGraphTools } from '../../agent/tools/graph-tools.js';
|
|
14
|
+
import { createSyncTools } from '../../agent/tools/sync-tools.js';
|
|
15
|
+
import { createFsTools } from '../../agent/tools/fs-tools.js';
|
|
16
|
+
import { loadSkillIndex, buildSkillIndexPrompt, loadEagerSkills } from '../../agent/skill-loader.js';
|
|
17
|
+
import { createLoadSkillTool } from '../../agent/tools/load-skill.js';
|
|
18
|
+
import { loadCustomTools } from '../../agent/tools/tool-loader.js';
|
|
19
|
+
import { ConnectorManager } from '../../mcp/connector-manager.js';
|
|
20
|
+
import { buildConnectorRegistry, createConnectorTools } from '../../agent/tools/connector-tools.js';
|
|
13
21
|
import { intro, select, text, outro, spinner } from '@clack/prompts';
|
|
14
22
|
import { runPreflight } from '../../utils/preflight.js';
|
|
15
|
-
import { loadUserConfig } from '../../core/user-config.js';
|
|
23
|
+
import { loadUserConfig, saveUserConfig, getAnthropicCredential } from '../../core/user-config.js';
|
|
24
|
+
import { notifyUpdateAvailable } from '../../utils/version-checker.js';
|
|
25
|
+
import { join, dirname } from 'path';
|
|
26
|
+
import { homedir } from 'os';
|
|
27
|
+
import { fileURLToPath } from 'url';
|
|
28
|
+
import { readFileSync, existsSync } from 'fs';
|
|
29
|
+
import { execSync } from 'child_process';
|
|
16
30
|
import * as readline from 'readline';
|
|
31
|
+
import { Markdown } from '@mariozechner/pi-tui';
|
|
32
|
+
import { info, dimText, header, warn, toolRef, renderUserPrompt } from '../colors.js';
|
|
33
|
+
import { box } from 'consola/utils';
|
|
34
|
+
import ora from 'ora';
|
|
35
|
+
import { markdownTheme } from '../theme.js';
|
|
17
36
|
// Model provider configurations
|
|
18
37
|
const MODEL_PROVIDERS = {
|
|
19
38
|
anthropic: {
|
|
@@ -91,14 +110,14 @@ export const startCommand = new Command('start')
|
|
|
91
110
|
return;
|
|
92
111
|
}
|
|
93
112
|
// Load workspace config and user config
|
|
94
|
-
const config = workspace.loadConfig();
|
|
95
|
-
const repos = workspace.getRepos();
|
|
113
|
+
const config = await workspace.loadConfig();
|
|
114
|
+
const repos = await workspace.getRepos();
|
|
96
115
|
const userConfig = loadUserConfig();
|
|
97
116
|
// Check if model is configured — user config takes precedence over workspace config
|
|
98
117
|
let modelProvider = userConfig.model_provider || config.model_provider;
|
|
99
118
|
let modelId = userConfig.model_id || config.model_id;
|
|
100
|
-
// API key: user config → workspace.json
|
|
101
|
-
let apiKey = userConfig
|
|
119
|
+
// API key: user config (oauth token or api key) → workspace.json → env var (resolved below)
|
|
120
|
+
let apiKey = getAnthropicCredential(userConfig) || config.api_key;
|
|
102
121
|
if (!modelProvider || !modelId) {
|
|
103
122
|
// First-time setup
|
|
104
123
|
intro('🎨 Studiograph Setup');
|
|
@@ -143,6 +162,27 @@ export const startCommand = new Command('start')
|
|
|
143
162
|
console.log(`✓ Using ${provider.envVar} from environment\n`);
|
|
144
163
|
apiKey = envKey;
|
|
145
164
|
}
|
|
165
|
+
else if (modelProvider === 'anthropic') {
|
|
166
|
+
// Anthropic requires API key from console.anthropic.com
|
|
167
|
+
const keyInput = await text({
|
|
168
|
+
message: 'Enter your Anthropic API key:',
|
|
169
|
+
placeholder: 'sk-ant-api03-...',
|
|
170
|
+
validate: (value) => {
|
|
171
|
+
if (!value || value.length < 10)
|
|
172
|
+
return 'API key seems too short';
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
if (typeof keyInput !== 'string') {
|
|
176
|
+
outro('Cancelled');
|
|
177
|
+
process.exit(0);
|
|
178
|
+
}
|
|
179
|
+
apiKey = keyInput;
|
|
180
|
+
const s = spinner();
|
|
181
|
+
s.start('Saving configuration...');
|
|
182
|
+
workspace.setModelConfig(modelProvider, modelId, apiKey);
|
|
183
|
+
s.stop('Configuration saved ✓');
|
|
184
|
+
outro(`Model configured: ${provider.name} - ${modelId}\n`);
|
|
185
|
+
}
|
|
146
186
|
else {
|
|
147
187
|
const keyInput = await text({
|
|
148
188
|
message: `Enter your ${provider.name} API key:`,
|
|
@@ -157,14 +197,21 @@ export const startCommand = new Command('start')
|
|
|
157
197
|
process.exit(0);
|
|
158
198
|
}
|
|
159
199
|
apiKey = keyInput;
|
|
200
|
+
const s = spinner();
|
|
201
|
+
s.start('Saving configuration...');
|
|
202
|
+
workspace.setModelConfig(modelProvider, modelId, apiKey);
|
|
203
|
+
s.stop('Configuration saved ✓');
|
|
204
|
+
outro(`Model configured: ${provider.name} - ${modelId}\n`);
|
|
160
205
|
}
|
|
161
206
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
207
|
+
else {
|
|
208
|
+
// Save configuration (non-Anthropic or key already available)
|
|
209
|
+
const s = spinner();
|
|
210
|
+
s.start('Saving configuration...');
|
|
211
|
+
workspace.setModelConfig(modelProvider, modelId, apiKey);
|
|
212
|
+
s.stop('Configuration saved ✓');
|
|
213
|
+
outro(`Model configured: ${provider.name} - ${modelId}\n`);
|
|
214
|
+
}
|
|
168
215
|
}
|
|
169
216
|
// Validate we have all required config
|
|
170
217
|
if (!modelProvider || !modelId) {
|
|
@@ -185,58 +232,162 @@ export const startCommand = new Command('start')
|
|
|
185
232
|
}
|
|
186
233
|
}
|
|
187
234
|
// Initialize memory service
|
|
188
|
-
const memoryService = new MemoryService(
|
|
235
|
+
const memoryService = new MemoryService();
|
|
189
236
|
const recentMemory = memoryService.loadRecentMemory();
|
|
190
|
-
//
|
|
191
|
-
const
|
|
237
|
+
// Initialize workspace manager
|
|
238
|
+
const gitUser = { id: 'cli-user', name: config.team_name, email: 'noreply@studiograph.local' };
|
|
239
|
+
const cliUser = { id: 'cli-user', name: config.team_name, email: 'noreply@studiograph.local', role: 'admin' };
|
|
240
|
+
const workspaceManager = new WorkspaceManager(process.cwd(), config, gitUser, cliUser);
|
|
241
|
+
// Build skill index from user > workspace > bundled directories
|
|
242
|
+
const startFileDir = dirname(fileURLToPath(import.meta.url));
|
|
243
|
+
const bundledSkillsDir = join(startFileDir, '../../agent/skills');
|
|
244
|
+
const userSkillsDir = join(homedir(), '.studiograph', 'skills');
|
|
245
|
+
const workspaceSkillsDir = join(process.cwd(), '.studiograph', 'skills');
|
|
246
|
+
const skillIndex = loadSkillIndex([userSkillsDir, workspaceSkillsDir, bundledSkillsDir]);
|
|
247
|
+
// Load ALL tools: graph + sync + skill loader
|
|
248
|
+
const tools = [
|
|
249
|
+
...createGraphTools(workspaceManager, gitUser, process.cwd()),
|
|
250
|
+
...createSyncTools(process.cwd(), config),
|
|
251
|
+
...createFsTools(),
|
|
252
|
+
createLoadSkillTool(skillIndex),
|
|
253
|
+
];
|
|
254
|
+
// Create system prompt with tool reference, skills, and memory
|
|
255
|
+
const systemPrompt = createSystemPrompt(config.team_name, repos, recentMemory, config.schema_extensions, tools, skillIndex);
|
|
192
256
|
// Initialize agent
|
|
193
257
|
const agent = new Agent({
|
|
194
258
|
getApiKey: () => apiKey,
|
|
195
259
|
});
|
|
196
|
-
// Set model and system prompt
|
|
197
260
|
const model = getModel(modelProvider, modelId);
|
|
198
261
|
agent.setModel(model);
|
|
199
262
|
agent.setSystemPrompt(systemPrompt);
|
|
200
|
-
// Load graph tools
|
|
201
|
-
const gitUser = { id: 'cli-user', name: config.team_name, email: 'noreply@studiograph.local' };
|
|
202
|
-
const cliUser = { id: 'cli-user', name: config.team_name, email: 'noreply@studiograph.local', role: 'admin' };
|
|
203
|
-
const workspaceManager = new WorkspaceManager(process.cwd(), config, gitUser, cliUser);
|
|
204
|
-
const tools = createGraphTools(workspaceManager, gitUser);
|
|
205
263
|
agent.setTools(tools);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
264
|
+
// Load custom tools and connector tools before starting the chat loop
|
|
265
|
+
const userToolsDir = join(homedir(), '.studiograph', 'tools');
|
|
266
|
+
const workspaceToolsDir = join(process.cwd(), '.studiograph', 'tools');
|
|
267
|
+
let connectorSummary = '';
|
|
268
|
+
try {
|
|
269
|
+
const [customTools, connectorTools] = await Promise.all([
|
|
270
|
+
loadCustomTools([userToolsDir, workspaceToolsDir], { workspaceManager, gitUser }),
|
|
271
|
+
ConnectorManager.buildTools(ConnectorManager.loadConfigs()),
|
|
272
|
+
]);
|
|
273
|
+
const extra = [...customTools];
|
|
274
|
+
// Connector tools go through a proxy registry (connector_list + connector_call)
|
|
275
|
+
// instead of registering hundreds of individual tools
|
|
276
|
+
if (connectorTools.length > 0) {
|
|
277
|
+
const registry = buildConnectorRegistry(connectorTools);
|
|
278
|
+
extra.push(...createConnectorTools(registry));
|
|
279
|
+
connectorSummary = [...registry.connectors.keys()].join(', ');
|
|
280
|
+
}
|
|
281
|
+
if (extra.length > 0) {
|
|
282
|
+
const existing = agent.state.tools;
|
|
283
|
+
agent.setTools([...existing, ...extra]);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
// Non-fatal — agent starts with core tools only
|
|
288
|
+
}
|
|
289
|
+
// Search index: first-run setup and staleness check
|
|
290
|
+
await initSearchIndex(workspaceManager, config.repos, process.cwd());
|
|
291
|
+
const totalTools = agent.state.tools.length;
|
|
292
|
+
const memoryDays = memoryService.listMemoryFiles().length;
|
|
293
|
+
console.log(box([
|
|
294
|
+
`Model: ${header(modelProvider + '/' + modelId)}`,
|
|
295
|
+
`Tools: ${totalTools} registered${connectorSummary ? ` (${connectorSummary})` : ''}`,
|
|
296
|
+
`Memory: ${memoryDays} day${memoryDays === 1 ? '' : 's'} loaded`,
|
|
297
|
+
].join('\n'), {
|
|
298
|
+
title: `Studiograph — ${config.team_name}`,
|
|
299
|
+
style: { borderColor: 'cyan', padding: 1 },
|
|
300
|
+
}));
|
|
301
|
+
// Check for updates (non-blocking, cached)
|
|
302
|
+
await notifyUpdateAvailable();
|
|
303
|
+
console.log(dimText('Type your message and press Enter. Type "exit" to quit.\n'));
|
|
211
304
|
// Track conversation for memory
|
|
212
305
|
const conversationLog = [];
|
|
213
|
-
// Track
|
|
214
|
-
let
|
|
215
|
-
// Track tool sequence for spacing
|
|
306
|
+
// Track whether any non-whitespace content has been written this turn
|
|
307
|
+
let hasWrittenOutput = false;
|
|
308
|
+
// Track whether we're in a consecutive tool sequence (for spacing)
|
|
216
309
|
let inToolSequence = false;
|
|
310
|
+
// Markdown rendering state — pi-tui Markdown component for styled output
|
|
311
|
+
let mdRenderer = new Markdown('', 1, 0, markdownTheme);
|
|
312
|
+
let fullText = '';
|
|
313
|
+
let displayedLineCount = 0;
|
|
314
|
+
let lastRenderedText = '';
|
|
315
|
+
// Collapse consecutive blank lines in rendered output
|
|
316
|
+
let lastLineWasBlank = false;
|
|
317
|
+
// Spinner state for tool execution
|
|
318
|
+
let toolSpinner = null;
|
|
217
319
|
// Subscribe to agent events
|
|
218
320
|
agent.subscribe((event) => {
|
|
219
321
|
if (event.type === 'message_start') {
|
|
220
|
-
|
|
322
|
+
// Reset per-turn output tracker but preserve inToolSequence across
|
|
323
|
+
// message boundaries so text following a tool turn gets a blank line.
|
|
324
|
+
hasWrittenOutput = false;
|
|
325
|
+
// Reset markdown rendering state for new message
|
|
326
|
+
mdRenderer = new Markdown('', 1, 0, markdownTheme);
|
|
327
|
+
fullText = '';
|
|
328
|
+
displayedLineCount = 0;
|
|
329
|
+
lastRenderedText = '';
|
|
330
|
+
lastLineWasBlank = false;
|
|
221
331
|
}
|
|
222
332
|
else if (event.type === 'message_update') {
|
|
223
333
|
const msgEvent = event.assistantMessageEvent;
|
|
224
|
-
if (msgEvent.type === 'text_delta') {
|
|
334
|
+
if (msgEvent && msgEvent.type === 'text_delta') {
|
|
225
335
|
let output = msgEvent.delta;
|
|
226
|
-
//
|
|
227
|
-
if (
|
|
228
|
-
output = output.replace(/^\
|
|
229
|
-
|
|
336
|
+
// Suppress whitespace-only deltas until real content appears.
|
|
337
|
+
if (!hasWrittenOutput) {
|
|
338
|
+
output = output.replace(/^\s+/, '');
|
|
339
|
+
if (!output)
|
|
340
|
+
return;
|
|
230
341
|
}
|
|
231
|
-
// Add
|
|
342
|
+
// Add breathing room before text that follows a tool sequence
|
|
232
343
|
if (inToolSequence) {
|
|
233
344
|
process.stdout.write('\n');
|
|
234
345
|
inToolSequence = false;
|
|
346
|
+
lastLineWasBlank = true; // prevent double blank after tool gap
|
|
347
|
+
}
|
|
348
|
+
hasWrittenOutput = true;
|
|
349
|
+
// Accumulate text and render complete lines through Markdown
|
|
350
|
+
fullText += output;
|
|
351
|
+
const lastNewline = fullText.lastIndexOf('\n');
|
|
352
|
+
if (lastNewline >= 0) {
|
|
353
|
+
const completeText = fullText.substring(0, lastNewline + 1);
|
|
354
|
+
if (completeText !== lastRenderedText) {
|
|
355
|
+
// Defer rendering while inside an unclosed code fence — pi-tui
|
|
356
|
+
// renders code blocks with borders, which shifts line indices
|
|
357
|
+
// and causes content to be skipped by the incremental display.
|
|
358
|
+
const fenceCount = (completeText.match(/^```/gm) || []).length;
|
|
359
|
+
if (fenceCount % 2 === 1)
|
|
360
|
+
return;
|
|
361
|
+
mdRenderer.setText(completeText);
|
|
362
|
+
const width = process.stdout.columns || 80;
|
|
363
|
+
const allLines = mdRenderer.render(width);
|
|
364
|
+
for (let i = displayedLineCount; i < allLines.length; i++) {
|
|
365
|
+
const isBlank = allLines[i].trim() === '';
|
|
366
|
+
if (isBlank && lastLineWasBlank)
|
|
367
|
+
continue;
|
|
368
|
+
lastLineWasBlank = isBlank;
|
|
369
|
+
process.stdout.write(allLines[i] + '\n');
|
|
370
|
+
}
|
|
371
|
+
displayedLineCount = allLines.length;
|
|
372
|
+
lastRenderedText = completeText;
|
|
373
|
+
}
|
|
235
374
|
}
|
|
236
|
-
process.stdout.write(output);
|
|
237
375
|
}
|
|
238
376
|
}
|
|
239
377
|
else if (event.type === 'message_end') {
|
|
378
|
+
// Render any remaining text (trailing incomplete line)
|
|
379
|
+
if (fullText && fullText !== lastRenderedText) {
|
|
380
|
+
mdRenderer.setText(fullText);
|
|
381
|
+
const width = process.stdout.columns || 80;
|
|
382
|
+
const allLines = mdRenderer.render(width);
|
|
383
|
+
for (let i = displayedLineCount; i < allLines.length; i++) {
|
|
384
|
+
const isBlank = allLines[i].trim() === '';
|
|
385
|
+
if (isBlank && lastLineWasBlank)
|
|
386
|
+
continue;
|
|
387
|
+
lastLineWasBlank = isBlank;
|
|
388
|
+
process.stdout.write(allLines[i] + '\n');
|
|
389
|
+
}
|
|
390
|
+
}
|
|
240
391
|
// Message complete - extract text for logging
|
|
241
392
|
const msg = event.message;
|
|
242
393
|
if (msg.role === 'assistant') {
|
|
@@ -246,29 +397,43 @@ export const startCommand = new Command('start')
|
|
|
246
397
|
.join('');
|
|
247
398
|
if (textContent) {
|
|
248
399
|
conversationLog.push(`Assistant: ${textContent}`);
|
|
400
|
+
// Reset all formatting, then one blank line before next prompt
|
|
401
|
+
process.stdout.write('\x1b[0m\n');
|
|
402
|
+
inToolSequence = false;
|
|
249
403
|
}
|
|
250
|
-
// Add blank line after assistant response before next prompt
|
|
251
|
-
process.stdout.write('\n\n');
|
|
252
404
|
}
|
|
253
405
|
}
|
|
254
406
|
else if (event.type === 'tool_execution_start') {
|
|
255
|
-
// Add blank line between consecutive tools (not before first)
|
|
256
407
|
if (inToolSequence) {
|
|
257
|
-
process.
|
|
408
|
+
process.stderr.write('\n');
|
|
258
409
|
}
|
|
259
410
|
else {
|
|
260
411
|
inToolSequence = true;
|
|
261
412
|
}
|
|
262
|
-
|
|
263
|
-
|
|
413
|
+
toolSpinner = ora({
|
|
414
|
+
text: info(`Using tool: ${toolRef(event.toolName)}`),
|
|
415
|
+
stream: process.stderr,
|
|
416
|
+
color: 'cyan',
|
|
417
|
+
}).start();
|
|
418
|
+
hasWrittenOutput = true;
|
|
419
|
+
}
|
|
420
|
+
else if (event.type === 'agent_end') {
|
|
421
|
+
// Agent completely done - fire terminal bell once
|
|
422
|
+
process.stdout.write('\x07');
|
|
264
423
|
}
|
|
265
424
|
else if (event.type === 'tool_execution_end') {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
425
|
+
if (toolSpinner) {
|
|
426
|
+
const toolLabel = info(`Using tool: ${toolRef(event.toolName)}`);
|
|
427
|
+
if (event.isError) {
|
|
428
|
+
toolSpinner.fail(warn(`${toolRef(event.toolName)}: Execution failed`));
|
|
429
|
+
}
|
|
430
|
+
else if (event.result?.details?.success === false) {
|
|
431
|
+
toolSpinner.fail(warn(`${toolRef(event.toolName)}: ${event.result.details.error || 'Unknown error'}`));
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
toolSpinner.stopAndPersist({ symbol: info('🔧'), text: toolLabel });
|
|
435
|
+
}
|
|
436
|
+
toolSpinner = null;
|
|
272
437
|
}
|
|
273
438
|
}
|
|
274
439
|
});
|
|
@@ -278,9 +443,19 @@ export const startCommand = new Command('start')
|
|
|
278
443
|
output: process.stdout,
|
|
279
444
|
prompt: '> ',
|
|
280
445
|
});
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
446
|
+
// ── Multiline paste handling ──────────────────────────────────────────────
|
|
447
|
+
// When text is pasted, readline fires 'line' for each \n in rapid
|
|
448
|
+
// succession. We collect lines that arrive within a short window and
|
|
449
|
+
// join them into a single message (like Claude Code).
|
|
450
|
+
const PASTE_DEBOUNCE_MS = 50;
|
|
451
|
+
let pasteBuffer = [];
|
|
452
|
+
let pasteTimer = null;
|
|
453
|
+
// ── Prompt serialization ──────────────────────────────────────────────────
|
|
454
|
+
// If the user pastes multiple chunks or types while the agent is busy,
|
|
455
|
+
// we queue messages and process them one at a time.
|
|
456
|
+
let agentBusy = false;
|
|
457
|
+
const messageQueue = [];
|
|
458
|
+
async function processMessage(message) {
|
|
284
459
|
if (message.toLowerCase() === 'exit') {
|
|
285
460
|
// Save session summary to memory
|
|
286
461
|
if (conversationLog.length > 0) {
|
|
@@ -295,13 +470,27 @@ export const startCommand = new Command('start')
|
|
|
295
470
|
return;
|
|
296
471
|
}
|
|
297
472
|
if (!message) {
|
|
473
|
+
process.stdout.write('\x1b[0m');
|
|
298
474
|
rl.prompt();
|
|
299
475
|
return;
|
|
300
476
|
}
|
|
477
|
+
agentBusy = true;
|
|
301
478
|
try {
|
|
302
479
|
conversationLog.push(`User: ${message}`);
|
|
303
|
-
//
|
|
304
|
-
|
|
480
|
+
// Re-render user input with dark gray background (overwrites readline echo)
|
|
481
|
+
// Account for multi-line wrapping: calculate how many terminal rows
|
|
482
|
+
// the readline echo occupied, then move up and clear each one.
|
|
483
|
+
const cols = process.stdout.columns || 80;
|
|
484
|
+
const echoLen = 2 + message.length; // "> " prompt + message
|
|
485
|
+
const wrappedLines = Math.ceil(echoLen / cols) || 1;
|
|
486
|
+
process.stdout.write(`\x1b[${wrappedLines}A`); // move up N lines
|
|
487
|
+
for (let i = 0; i < wrappedLines; i++) {
|
|
488
|
+
process.stdout.write('\x1b[2K'); // clear current line
|
|
489
|
+
if (i < wrappedLines - 1)
|
|
490
|
+
process.stdout.write('\x1b[1B'); // move down
|
|
491
|
+
}
|
|
492
|
+
process.stdout.write('\x1b[G'); // move to column 1
|
|
493
|
+
process.stdout.write(renderUserPrompt(message) + '\n\n');
|
|
305
494
|
// Send message to agent and wait for completion
|
|
306
495
|
await agent.prompt(message);
|
|
307
496
|
await agent.waitForIdle();
|
|
@@ -312,7 +501,46 @@ export const startCommand = new Command('start')
|
|
|
312
501
|
console.error('Stack trace:', error.stack);
|
|
313
502
|
}
|
|
314
503
|
}
|
|
315
|
-
|
|
504
|
+
agentBusy = false;
|
|
505
|
+
// Drain the queue
|
|
506
|
+
if (messageQueue.length > 0) {
|
|
507
|
+
const next = messageQueue.shift();
|
|
508
|
+
process.stdout.write('\x1b[0m');
|
|
509
|
+
rl.prompt();
|
|
510
|
+
// Small delay so the prompt renders before processing
|
|
511
|
+
setImmediate(() => processMessage(next));
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
process.stdout.write('\x1b[0m');
|
|
515
|
+
rl.prompt();
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
function flushPasteBuffer() {
|
|
519
|
+
pasteTimer = null;
|
|
520
|
+
const message = pasteBuffer.join('\n').trim();
|
|
521
|
+
pasteBuffer = [];
|
|
522
|
+
if (!message) {
|
|
523
|
+
if (!agentBusy) {
|
|
524
|
+
process.stdout.write('\x1b[0m');
|
|
525
|
+
rl.prompt();
|
|
526
|
+
}
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (agentBusy) {
|
|
530
|
+
messageQueue.push(message);
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
processMessage(message);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
rl.prompt();
|
|
537
|
+
rl.on('line', (input) => {
|
|
538
|
+
pasteBuffer.push(input);
|
|
539
|
+
// Reset the debounce timer — if another line arrives within
|
|
540
|
+
// PASTE_DEBOUNCE_MS it's part of the same paste operation
|
|
541
|
+
if (pasteTimer)
|
|
542
|
+
clearTimeout(pasteTimer);
|
|
543
|
+
pasteTimer = setTimeout(flushPasteBuffer, PASTE_DEBOUNCE_MS);
|
|
316
544
|
});
|
|
317
545
|
rl.on('close', () => {
|
|
318
546
|
console.log('\n👋 Goodbye!');
|
|
@@ -320,62 +548,203 @@ export const startCommand = new Command('start')
|
|
|
320
548
|
});
|
|
321
549
|
});
|
|
322
550
|
/**
|
|
323
|
-
*
|
|
551
|
+
* Initialize the search index on startup.
|
|
552
|
+
*
|
|
553
|
+
* First run: prompts for Voyage AI key (optional), then runs full index.
|
|
554
|
+
* Subsequent runs: checks git log for commits since last index and re-indexes changed entities.
|
|
324
555
|
*/
|
|
325
|
-
function
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
556
|
+
async function initSearchIndex(workspaceManager, repos, workspacePath) {
|
|
557
|
+
const userConfig = loadUserConfig();
|
|
558
|
+
const dbPath = userConfig.vector_db_path || join(homedir(), '.studiograph', 'vector');
|
|
559
|
+
const vectorService = workspaceManager.vectorService;
|
|
560
|
+
// First-run: no repos have been indexed yet
|
|
561
|
+
const isFirstRun = repos.every(r => !vectorService.getLastIndexedTime(r.name));
|
|
562
|
+
if (isFirstRun) {
|
|
563
|
+
// Prompt for Voyage AI key (optional)
|
|
564
|
+
if (!userConfig.voyage_api_key) {
|
|
565
|
+
const choice = await select({
|
|
566
|
+
message: 'Enable semantic search? (optional)',
|
|
567
|
+
options: [
|
|
568
|
+
{ value: 'skip', label: 'Skip — use FTS keyword search', hint: 'No setup required' },
|
|
569
|
+
{ value: 'voyage', label: 'Enable semantic search', hint: 'Voyage AI key required' },
|
|
570
|
+
],
|
|
571
|
+
});
|
|
572
|
+
if (choice === 'voyage' && typeof choice === 'string') {
|
|
573
|
+
const keyInput = await text({
|
|
574
|
+
message: 'Enter your Voyage AI API key:',
|
|
575
|
+
placeholder: 'pa-...',
|
|
576
|
+
validate: (v) => (!v || v.length < 10 ? 'Key seems too short' : undefined),
|
|
577
|
+
});
|
|
578
|
+
if (typeof keyInput === 'string') {
|
|
579
|
+
userConfig.voyage_api_key = keyInput;
|
|
580
|
+
saveUserConfig(userConfig);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
// Full index on first run
|
|
585
|
+
const s = spinner();
|
|
586
|
+
s.start('Indexing workspace for search...');
|
|
587
|
+
let total = 0;
|
|
588
|
+
for (const repo of repos) {
|
|
589
|
+
const repoPath = join(workspacePath, repo.path);
|
|
590
|
+
try {
|
|
591
|
+
const graph = workspaceManager['graphs'].get(repo.name);
|
|
592
|
+
if (!graph)
|
|
593
|
+
continue;
|
|
594
|
+
const entities = graph.list();
|
|
595
|
+
if (entities.length > 0) {
|
|
596
|
+
await vectorService.upsertMany(repo.name, entities);
|
|
597
|
+
total += entities.length;
|
|
598
|
+
}
|
|
599
|
+
vectorService.setLastIndexedTime(repo.name);
|
|
600
|
+
}
|
|
601
|
+
catch { /* non-fatal */ }
|
|
602
|
+
}
|
|
603
|
+
s.stop(`Search index ready (${total} entities)`);
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
// Subsequent runs: check git log for changes since last index
|
|
607
|
+
const staleEntities = [];
|
|
608
|
+
for (const repo of repos) {
|
|
609
|
+
const lastIndexed = vectorService.getLastIndexedTime(repo.name);
|
|
610
|
+
if (!lastIndexed)
|
|
611
|
+
continue;
|
|
612
|
+
const repoPath = join(workspacePath, repo.path);
|
|
613
|
+
try {
|
|
614
|
+
const output = execSync(`git log --since="${lastIndexed}" --name-status --format="" -- "*.md"`, { cwd: repoPath, encoding: 'utf-8', stdio: 'pipe' }).trim();
|
|
615
|
+
if (!output)
|
|
616
|
+
continue;
|
|
617
|
+
for (const line of output.split('\n')) {
|
|
618
|
+
const match = line.match(/^([AMD])\s+(\w+)\/([^/]+)\.md$/);
|
|
619
|
+
if (!match)
|
|
620
|
+
continue;
|
|
621
|
+
const [, status, entityType, entityId] = match;
|
|
622
|
+
staleEntities.push({ repoName: repo.name, entityType, entityId, deleted: status === 'D' });
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
catch { /* non-fatal */ }
|
|
626
|
+
}
|
|
627
|
+
if (staleEntities.length === 0)
|
|
628
|
+
return;
|
|
629
|
+
const s = spinner();
|
|
630
|
+
s.start(`Updating search index (${staleEntities.length} change${staleEntities.length === 1 ? '' : 's'})...`);
|
|
631
|
+
for (const item of staleEntities) {
|
|
632
|
+
try {
|
|
633
|
+
if (item.deleted) {
|
|
634
|
+
await vectorService.delete(item.repoName, item.entityType, item.entityId);
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
const graph = workspaceManager['graphs'].get(item.repoName);
|
|
638
|
+
if (!graph)
|
|
639
|
+
continue;
|
|
640
|
+
const entity = graph.get(item.entityType, item.entityId);
|
|
641
|
+
await vectorService.upsert(item.repoName, entity);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
catch { /* entity may not exist — skip */ }
|
|
645
|
+
}
|
|
646
|
+
for (const repo of repos) {
|
|
647
|
+
vectorService.setLastIndexedTime(repo.name);
|
|
648
|
+
}
|
|
649
|
+
s.stop('Search index updated');
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Create system prompt with workspace context, tool reference, skills, and memory.
|
|
653
|
+
*/
|
|
654
|
+
function createSystemPrompt(teamName, repos, recentMemory, schemaExtensions, tools, skillIndex) {
|
|
655
|
+
// Load the authoritative system prompt from system.md
|
|
656
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
657
|
+
const __dirname = dirname(__filename);
|
|
658
|
+
const promptPath = join(__dirname, '../../agent/prompts/system.md');
|
|
659
|
+
let base = existsSync(promptPath)
|
|
660
|
+
? readFileSync(promptPath, 'utf-8')
|
|
661
|
+
: 'You are Studiograph, an AI agent that helps creative teams manage their knowledge graphs.';
|
|
662
|
+
// Auto-generate tool reference from registered tools, grouped by category
|
|
663
|
+
if (tools && tools.length > 0) {
|
|
664
|
+
const categories = {
|
|
665
|
+
'Entity Management': [],
|
|
666
|
+
'Data Sync & Source Config': [],
|
|
667
|
+
'Enrichment': [],
|
|
668
|
+
'Workspace Git': [],
|
|
669
|
+
'External Connectors': [],
|
|
670
|
+
'Skills': [],
|
|
671
|
+
'Other': [],
|
|
672
|
+
};
|
|
673
|
+
for (const t of tools) {
|
|
674
|
+
if (t.description?.startsWith('DEPRECATED'))
|
|
675
|
+
continue;
|
|
676
|
+
const name = t.name ?? '';
|
|
677
|
+
if (name.startsWith('sync_'))
|
|
678
|
+
categories['Data Sync & Source Config'].push(t);
|
|
679
|
+
else if (name.startsWith('enrich_'))
|
|
680
|
+
categories['Enrichment'].push(t);
|
|
681
|
+
else if (name.startsWith('workspace_'))
|
|
682
|
+
categories['Workspace Git'].push(t);
|
|
683
|
+
else if (name.startsWith('connector_'))
|
|
684
|
+
categories['External Connectors'].push(t);
|
|
685
|
+
else if (name === 'load_skill')
|
|
686
|
+
categories['Skills'].push(t);
|
|
687
|
+
else if (['create_entity', 'get_entity', 'update_entity', 'delete_entity', 'search_entities', 'list_entities', 'get_related', 'get_backlinks', 'validate_wikilinks', 'get_workspace_summary', 'list_repos', 'query_dataset', 'append_rows', 'get_entity_schema', 'lint_entities'].includes(name))
|
|
688
|
+
categories['Entity Management'].push(t);
|
|
689
|
+
else
|
|
690
|
+
categories['Other'].push(t);
|
|
691
|
+
}
|
|
692
|
+
const toolRef = Object.entries(categories)
|
|
693
|
+
.filter(([, items]) => items.length > 0)
|
|
694
|
+
.map(([cat, items]) => `**${cat}:**\n${items.map((t) => `- \`${t.name}\` — ${t.description}`).join('\n')}`)
|
|
695
|
+
.join('\n\n');
|
|
696
|
+
base = base.replace('{{TOOL_REFERENCE}}', toolRef);
|
|
697
|
+
}
|
|
698
|
+
else {
|
|
699
|
+
base = base.replace('{{TOOL_REFERENCE}}', '');
|
|
700
|
+
}
|
|
701
|
+
const registry = new SchemaRegistry(schemaExtensions);
|
|
702
|
+
const schemaSection = registry.toPromptSection();
|
|
703
|
+
const workspaceSection = `## Workspace: ${teamName}
|
|
329
704
|
|
|
330
705
|
You are working in a Studiograph workspace with the following repositories:
|
|
331
706
|
|
|
332
|
-
${repos.map(repo => `- ${repo.name} (${repo.type}): ${repo.description || 'No description'}`).join('\n')}
|
|
707
|
+
${repos.map(repo => `- ${repo.name} (${repo.type}): ${repo.description || 'No description'}`).join('\n')}`;
|
|
708
|
+
const memorySection = recentMemory
|
|
709
|
+
? `## Memory Context\n\n${recentMemory}`
|
|
710
|
+
: '';
|
|
711
|
+
const outputSection = `## Output Formatting
|
|
333
712
|
|
|
334
|
-
|
|
713
|
+
You are running in a CLI terminal. Keep responses **short and scannable**.
|
|
335
714
|
|
|
336
|
-
|
|
715
|
+
**Length rules:**
|
|
716
|
+
- Default to 2-4 sentences for simple answers
|
|
717
|
+
- Use bullet lists for 3+ items — never write items as prose paragraphs
|
|
718
|
+
- Limit tool result summaries to the most relevant 5-10 items
|
|
719
|
+
- Only use headings (##) when the response has 2+ distinct sections
|
|
720
|
+
- If the user asks a yes/no question, lead with the answer
|
|
337
721
|
|
|
338
|
-
|
|
722
|
+
**Formatting rules:**
|
|
723
|
+
- Use **bold** for emphasis and \`inline code\` for commands, entity types, and field names
|
|
724
|
+
- Use fenced code blocks for multi-line code, YAML, or file contents
|
|
725
|
+
- Use > blockquotes sparingly for callouts
|
|
726
|
+
- NEVER use markdown tables (| pipes) — use bullet lists instead
|
|
727
|
+
- Do NOT repeat information the user already knows
|
|
339
728
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
- Ask clarifying questions when needed
|
|
361
|
-
- Suggest related entities and connections
|
|
362
|
-
- Track important decisions and context in memory
|
|
363
|
-
- Always save significant insights to memory for future sessions
|
|
364
|
-
|
|
365
|
-
# Output Formatting
|
|
366
|
-
|
|
367
|
-
IMPORTANT: You are running in a CLI (command-line interface). Use plain text formatting:
|
|
368
|
-
- NO markdown syntax: no bold (**text**), italic (*text*), code formatting (\`code\`), headings (# Heading), or tables
|
|
369
|
-
- For section headers, use ALL CAPS on a single line with NO blank line after:
|
|
370
|
-
Example:
|
|
371
|
-
MANAGE YOUR KNOWLEDGE
|
|
372
|
-
- Create entities
|
|
373
|
-
- Search and discover
|
|
374
|
-
- Use simple bullet points with single hyphens: "- item"
|
|
375
|
-
- For punctuation, use single hyphens (-) not double hyphens (--)
|
|
376
|
-
- Separate different sections with ONE blank line between them
|
|
377
|
-
- Keep responses concise and conversational
|
|
378
|
-
|
|
379
|
-
Start by greeting the user and asking how you can help them today.`;
|
|
729
|
+
Start by greeting the user briefly and asking how you can help.`;
|
|
730
|
+
const parts = [base.trim(), workspaceSection];
|
|
731
|
+
if (memorySection)
|
|
732
|
+
parts.push(memorySection);
|
|
733
|
+
if (schemaSection)
|
|
734
|
+
parts.push(schemaSection);
|
|
735
|
+
// Inline eager skills directly into the system prompt
|
|
736
|
+
if (skillIndex) {
|
|
737
|
+
const eagerContent = loadEagerSkills(skillIndex);
|
|
738
|
+
if (eagerContent)
|
|
739
|
+
parts.push(eagerContent);
|
|
740
|
+
}
|
|
741
|
+
// Append skill index for on-demand skills
|
|
742
|
+
if (skillIndex) {
|
|
743
|
+
const index = buildSkillIndexPrompt(skillIndex);
|
|
744
|
+
if (index)
|
|
745
|
+
parts.push(index);
|
|
746
|
+
}
|
|
747
|
+
parts.push(outputSection);
|
|
748
|
+
return parts.join('\n\n');
|
|
380
749
|
}
|
|
381
750
|
//# sourceMappingURL=start.js.map
|