@rkarim08/sia 1.0.0
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/.claude-plugin/marketplace.json +35 -0
- package/.claude-plugin/plugin.json +27 -0
- package/.mcp.json +13 -0
- package/CLAUDE.md +226 -0
- package/LICENSE +202 -0
- package/PLUGIN_README.md +253 -0
- package/README.md +1013 -0
- package/agents/sia-changelog-writer.md +89 -0
- package/agents/sia-code-reviewer.md +86 -0
- package/agents/sia-conflict-resolver.md +100 -0
- package/agents/sia-convention-enforcer.md +69 -0
- package/agents/sia-debug.md +106 -0
- package/agents/sia-decision-reviewer.md +101 -0
- package/agents/sia-dependency-tracker.md +80 -0
- package/agents/sia-explain.md +126 -0
- package/agents/sia-feature.md +116 -0
- package/agents/sia-knowledge-capture.md +117 -0
- package/agents/sia-lead-architecture-advisor.md +93 -0
- package/agents/sia-lead-team-health.md +107 -0
- package/agents/sia-migration.md +100 -0
- package/agents/sia-onboarding.md +115 -0
- package/agents/sia-orientation.md +99 -0
- package/agents/sia-pm-briefing.md +106 -0
- package/agents/sia-pm-risk-advisor.md +82 -0
- package/agents/sia-qa-analyst.md +116 -0
- package/agents/sia-qa-regression-map.md +94 -0
- package/agents/sia-refactor.md +115 -0
- package/agents/sia-regression.md +112 -0
- package/agents/sia-security-audit.md +125 -0
- package/agents/sia-test-advisor.md +91 -0
- package/hooks/hooks.json +98 -0
- package/migrations/bridge/001_initial.sql +34 -0
- package/migrations/episodic/001_initial.sql +35 -0
- package/migrations/meta/001_initial.sql +68 -0
- package/migrations/semantic/001_initial.sql +292 -0
- package/migrations/semantic/002_ontology.sql +89 -0
- package/migrations/semantic/003_freshness.sql +63 -0
- package/migrations/semantic/004_v5_unified_schema.sql +194 -0
- package/migrations/semantic/005_backfill_event_kinds.sql +8 -0
- package/migrations/semantic/006_tree_sitter.sql +6 -0
- package/migrations/semantic/007_branch_snapshots.sql +22 -0
- package/package.json +110 -0
- package/scripts/branch-switch.sh +13 -0
- package/scripts/build-wasm-grammars.sh +81 -0
- package/scripts/post-compact.sh +8 -0
- package/scripts/post-tool-use.sh +10 -0
- package/scripts/pre-compact.sh +8 -0
- package/scripts/session-end.sh +8 -0
- package/scripts/session-start.sh +8 -0
- package/scripts/start-mcp.ts +45 -0
- package/scripts/stop-hook.sh +8 -0
- package/scripts/user-prompt-submit.sh +8 -0
- package/scripts/viz-server.ts +152 -0
- package/skills/sia-brainstorm/SKILL.md +156 -0
- package/skills/sia-brainstorm/scripts/frame-template.html +214 -0
- package/skills/sia-brainstorm/scripts/helper.js +95 -0
- package/skills/sia-brainstorm/scripts/server.cjs +338 -0
- package/skills/sia-brainstorm/scripts/start-server.sh +153 -0
- package/skills/sia-brainstorm/scripts/stop-server.sh +55 -0
- package/skills/sia-brainstorm/spec-document-reviewer-prompt.md +49 -0
- package/skills/sia-brainstorm/visual-companion.md +286 -0
- package/skills/sia-capture/SKILL.md +64 -0
- package/skills/sia-compare/SKILL.md +33 -0
- package/skills/sia-conflicts/SKILL.md +38 -0
- package/skills/sia-debug-workflow/SKILL.md +120 -0
- package/skills/sia-debug-workflow/root-cause-tracing.md +70 -0
- package/skills/sia-debug-workflow/scripts/find-polluter.sh +64 -0
- package/skills/sia-debug-workflow/temporal-investigation.md +72 -0
- package/skills/sia-digest/SKILL.md +23 -0
- package/skills/sia-dispatch/SKILL.md +69 -0
- package/skills/sia-dispatch/agent-task-template.md +99 -0
- package/skills/sia-doctor/SKILL.md +39 -0
- package/skills/sia-execute/SKILL.md +70 -0
- package/skills/sia-execute-plan/SKILL.md +85 -0
- package/skills/sia-export-import/SKILL.md +49 -0
- package/skills/sia-export-knowledge/SKILL.md +46 -0
- package/skills/sia-finish/SKILL.md +100 -0
- package/skills/sia-finish/pr-summary-template.md +54 -0
- package/skills/sia-freshness/SKILL.md +38 -0
- package/skills/sia-history/SKILL.md +42 -0
- package/skills/sia-impact/SKILL.md +70 -0
- package/skills/sia-index/SKILL.md +54 -0
- package/skills/sia-install/SKILL.md +39 -0
- package/skills/sia-lead-compliance/SKILL.md +16 -0
- package/skills/sia-lead-drift-report/SKILL.md +16 -0
- package/skills/sia-lead-knowledge-map/SKILL.md +16 -0
- package/skills/sia-learn/SKILL.md +58 -0
- package/skills/sia-plan/SKILL.md +68 -0
- package/skills/sia-plan/plan-reviewer-prompt.md +63 -0
- package/skills/sia-playbooks/SKILL.md +29 -0
- package/skills/sia-playbooks/reference-feature.md +100 -0
- package/skills/sia-playbooks/reference-flagging.md +50 -0
- package/skills/sia-playbooks/reference-orientation.md +92 -0
- package/skills/sia-playbooks/reference-regression.md +115 -0
- package/skills/sia-playbooks/reference-review.md +64 -0
- package/skills/sia-playbooks/reference-tools.md +239 -0
- package/skills/sia-pm-decision-log/SKILL.md +28 -0
- package/skills/sia-pm-risk-dashboard/SKILL.md +24 -0
- package/skills/sia-pm-sprint-summary/SKILL.md +27 -0
- package/skills/sia-prune/SKILL.md +45 -0
- package/skills/sia-qa-coverage/SKILL.md +28 -0
- package/skills/sia-qa-flaky/SKILL.md +20 -0
- package/skills/sia-qa-report/SKILL.md +26 -0
- package/skills/sia-reindex/SKILL.md +30 -0
- package/skills/sia-review-respond/SKILL.md +88 -0
- package/skills/sia-review-respond/pushback-patterns.md +90 -0
- package/skills/sia-search/SKILL.md +47 -0
- package/skills/sia-setup/SKILL.md +82 -0
- package/skills/sia-setup/setup-checklist.md +97 -0
- package/skills/sia-stats/SKILL.md +36 -0
- package/skills/sia-status/SKILL.md +44 -0
- package/skills/sia-sync/SKILL.md +46 -0
- package/skills/sia-team/SKILL.md +64 -0
- package/skills/sia-test/SKILL.md +92 -0
- package/skills/sia-test/testing-anti-patterns.md +104 -0
- package/skills/sia-tour/SKILL.md +29 -0
- package/skills/sia-upgrade/SKILL.md +43 -0
- package/skills/sia-verify/SKILL.md +81 -0
- package/skills/sia-visualize/SKILL.md +28 -0
- package/skills/sia-visualize-live/SKILL.md +55 -0
- package/skills/sia-visualize-live/scripts/graph-template.html +389 -0
- package/skills/sia-visualize-live/scripts/start-visualizer.sh +161 -0
- package/skills/sia-visualize-live/scripts/stop-visualizer.sh +55 -0
- package/skills/sia-visualize-live/scripts/visualizer-server.cjs +264 -0
- package/skills/sia-workspace/SKILL.md +57 -0
- package/src/agent/claude-md-template-flagging.md +219 -0
- package/src/agent/claude-md-template.md +213 -0
- package/src/agent/modules/sia-feature.md +100 -0
- package/src/agent/modules/sia-flagging.md +50 -0
- package/src/agent/modules/sia-orientation.md +92 -0
- package/src/agent/modules/sia-regression.md +115 -0
- package/src/agent/modules/sia-review.md +64 -0
- package/src/agent/modules/sia-tools.md +239 -0
- package/src/ast/extractors/c-include.ts +189 -0
- package/src/ast/extractors/csharp-project.ts +260 -0
- package/src/ast/extractors/prisma-schema.ts +44 -0
- package/src/ast/extractors/project-manifest.ts +111 -0
- package/src/ast/extractors/sql-schema.ts +67 -0
- package/src/ast/extractors/tier-a.ts +423 -0
- package/src/ast/extractors/tier-b.ts +289 -0
- package/src/ast/extractors/tier-dispatch.ts +247 -0
- package/src/ast/index-worker.ts +108 -0
- package/src/ast/indexer.ts +484 -0
- package/src/ast/languages.ts +408 -0
- package/src/ast/pagerank-builder.ts +125 -0
- package/src/ast/path-utils.ts +137 -0
- package/src/ast/tree-sitter/backends/native.ts +57 -0
- package/src/ast/tree-sitter/backends/wasm.ts +39 -0
- package/src/ast/tree-sitter/call-walker.ts +44 -0
- package/src/ast/tree-sitter/edit-computer.ts +55 -0
- package/src/ast/tree-sitter/query-runner.ts +46 -0
- package/src/ast/tree-sitter/service.ts +174 -0
- package/src/ast/tree-sitter/tree-cache.ts +39 -0
- package/src/ast/tree-sitter/types.ts +79 -0
- package/src/ast/watcher.ts +322 -0
- package/src/capture/chunker.ts +169 -0
- package/src/capture/consolidate.ts +127 -0
- package/src/capture/edge-inferrer.ts +161 -0
- package/src/capture/embedder.ts +166 -0
- package/src/capture/embedding-cache.ts +73 -0
- package/src/capture/flag-processor.ts +64 -0
- package/src/capture/hook.ts +67 -0
- package/src/capture/pipeline.ts +450 -0
- package/src/capture/prompts/consolidate.ts +25 -0
- package/src/capture/prompts/edge-infer.ts +29 -0
- package/src/capture/prompts/extract-flagged.ts +36 -0
- package/src/capture/prompts/extract.ts +42 -0
- package/src/capture/tokenizer.ts +147 -0
- package/src/capture/track-a-ast.ts +93 -0
- package/src/capture/track-b-llm.ts +149 -0
- package/src/capture/types.ts +64 -0
- package/src/cli/commands/community.ts +137 -0
- package/src/cli/commands/compare.ts +123 -0
- package/src/cli/commands/conflicts.ts +41 -0
- package/src/cli/commands/digest.ts +197 -0
- package/src/cli/commands/disable-flagging.ts +34 -0
- package/src/cli/commands/doctor.ts +240 -0
- package/src/cli/commands/download-model.ts +161 -0
- package/src/cli/commands/enable-flagging.ts +34 -0
- package/src/cli/commands/export-knowledge.ts +208 -0
- package/src/cli/commands/export.ts +85 -0
- package/src/cli/commands/freshness.ts +164 -0
- package/src/cli/commands/graph.ts +51 -0
- package/src/cli/commands/history.ts +139 -0
- package/src/cli/commands/import.ts +335 -0
- package/src/cli/commands/install.ts +156 -0
- package/src/cli/commands/lead-report.ts +241 -0
- package/src/cli/commands/learn.ts +321 -0
- package/src/cli/commands/pm-report.ts +413 -0
- package/src/cli/commands/prune.ts +75 -0
- package/src/cli/commands/qa-report.ts +278 -0
- package/src/cli/commands/reindex.ts +104 -0
- package/src/cli/commands/rollback.ts +70 -0
- package/src/cli/commands/search.ts +103 -0
- package/src/cli/commands/server.ts +91 -0
- package/src/cli/commands/share.ts +33 -0
- package/src/cli/commands/stats.ts +79 -0
- package/src/cli/commands/status.ts +176 -0
- package/src/cli/commands/sync.ts +96 -0
- package/src/cli/commands/team.ts +118 -0
- package/src/cli/commands/tour.ts +157 -0
- package/src/cli/commands/visualize-live.ts +162 -0
- package/src/cli/commands/workspace.ts +117 -0
- package/src/cli/index.ts +424 -0
- package/src/cli/learn-progress.ts +87 -0
- package/src/community/detection-bridge.ts +344 -0
- package/src/community/leiden.ts +462 -0
- package/src/community/raptor.ts +210 -0
- package/src/community/scheduler.ts +74 -0
- package/src/community/summarize.ts +115 -0
- package/src/decay/archiver.ts +73 -0
- package/src/decay/bridge-orphan-cleanup.ts +212 -0
- package/src/decay/consolidation-sweep.ts +112 -0
- package/src/decay/decay.ts +116 -0
- package/src/decay/deep-validator.ts +62 -0
- package/src/decay/episodic-promoter.ts +132 -0
- package/src/decay/maintenance-scheduler.ts +326 -0
- package/src/decay/scheduler.ts +6 -0
- package/src/decay/session-sweeper.ts +79 -0
- package/src/decay/types.ts +17 -0
- package/src/freshness/confidence-decay.ts +122 -0
- package/src/freshness/cuckoo-filter.ts +176 -0
- package/src/freshness/deep-validation.ts +345 -0
- package/src/freshness/dirty-tracker.ts +237 -0
- package/src/freshness/file-watcher-layer.ts +119 -0
- package/src/freshness/firewall.ts +64 -0
- package/src/freshness/git-reconcile-layer.ts +161 -0
- package/src/freshness/inverted-index.ts +158 -0
- package/src/freshness/stale-read-layer.ts +222 -0
- package/src/graph/audit.ts +69 -0
- package/src/graph/bridge-db.ts +141 -0
- package/src/graph/communities.ts +195 -0
- package/src/graph/db-interface.ts +259 -0
- package/src/graph/edges.ts +163 -0
- package/src/graph/entities.ts +327 -0
- package/src/graph/episodic-db.ts +113 -0
- package/src/graph/flags.ts +31 -0
- package/src/graph/meta-db.ts +200 -0
- package/src/graph/semantic-db.ts +101 -0
- package/src/graph/session-resume.ts +56 -0
- package/src/graph/snapshots.ts +342 -0
- package/src/graph/staging.ts +151 -0
- package/src/graph/types.ts +128 -0
- package/src/hooks/adapters/claude-code.ts +21 -0
- package/src/hooks/adapters/cline.ts +43 -0
- package/src/hooks/adapters/cursor.ts +65 -0
- package/src/hooks/adapters/generic.ts +12 -0
- package/src/hooks/agent-detect.ts +34 -0
- package/src/hooks/claude-md-directives.ts +32 -0
- package/src/hooks/event-router.ts +182 -0
- package/src/hooks/extractors/pattern-detector.ts +111 -0
- package/src/hooks/handlers/post-compact.ts +30 -0
- package/src/hooks/handlers/post-tool-use.ts +403 -0
- package/src/hooks/handlers/pre-compact.ts +100 -0
- package/src/hooks/handlers/session-end.ts +47 -0
- package/src/hooks/handlers/session-start.ts +154 -0
- package/src/hooks/handlers/stop.ts +128 -0
- package/src/hooks/handlers/user-prompt-submit.ts +68 -0
- package/src/hooks/plugin-branch-switch.ts +68 -0
- package/src/hooks/plugin-common.ts +47 -0
- package/src/hooks/plugin-post-compact.ts +28 -0
- package/src/hooks/plugin-post-tool-use.ts +38 -0
- package/src/hooks/plugin-pre-compact.ts +37 -0
- package/src/hooks/plugin-session-end.ts +37 -0
- package/src/hooks/plugin-session-start.ts +75 -0
- package/src/hooks/plugin-stop.ts +61 -0
- package/src/hooks/plugin-user-prompt-submit.ts +47 -0
- package/src/hooks/types.ts +43 -0
- package/src/knowledge/discovery.ts +238 -0
- package/src/knowledge/external-refs.ts +98 -0
- package/src/knowledge/freshness.ts +221 -0
- package/src/knowledge/ingest.ts +330 -0
- package/src/knowledge/markdown-export.ts +229 -0
- package/src/knowledge/markdown-import.ts +359 -0
- package/src/knowledge/patterns.ts +74 -0
- package/src/knowledge/templates.ts +307 -0
- package/src/llm/ai-sdk-adapter.ts +46 -0
- package/src/llm/config.ts +88 -0
- package/src/llm/cost-tracker.ts +110 -0
- package/src/llm/prompts/extraction.ts +55 -0
- package/src/llm/prompts/summarization.ts +36 -0
- package/src/llm/prompts/validation.ts +37 -0
- package/src/llm/provider-registry.ts +68 -0
- package/src/llm/reliability.ts +179 -0
- package/src/llm/schemas.ts +52 -0
- package/src/mcp/freshness-annotator.ts +69 -0
- package/src/mcp/server.ts +949 -0
- package/src/mcp/tools/sia-ast-query.ts +225 -0
- package/src/mcp/tools/sia-at-time.ts +151 -0
- package/src/mcp/tools/sia-backlinks.ts +87 -0
- package/src/mcp/tools/sia-batch-execute.ts +169 -0
- package/src/mcp/tools/sia-by-file.ts +89 -0
- package/src/mcp/tools/sia-community.ts +113 -0
- package/src/mcp/tools/sia-doctor.ts +73 -0
- package/src/mcp/tools/sia-execute-file.ts +122 -0
- package/src/mcp/tools/sia-execute.ts +104 -0
- package/src/mcp/tools/sia-expand.ts +158 -0
- package/src/mcp/tools/sia-fetch-and-index.ts +241 -0
- package/src/mcp/tools/sia-flag.ts +65 -0
- package/src/mcp/tools/sia-index.ts +111 -0
- package/src/mcp/tools/sia-note.ts +134 -0
- package/src/mcp/tools/sia-search.ts +105 -0
- package/src/mcp/tools/sia-stats.ts +63 -0
- package/src/mcp/tools/sia-sync-status.ts +44 -0
- package/src/mcp/tools/sia-upgrade.ts +247 -0
- package/src/mcp/truncate.ts +231 -0
- package/src/native/bridge.ts +167 -0
- package/src/native/fallback-ast-diff.ts +144 -0
- package/src/native/fallback-graph.ts +325 -0
- package/src/ontology/constraints.ts +56 -0
- package/src/ontology/errors.ts +8 -0
- package/src/ontology/middleware.ts +266 -0
- package/src/retrieval/bm25-search.ts +151 -0
- package/src/retrieval/context-assembly.ts +76 -0
- package/src/retrieval/graph-traversal.ts +168 -0
- package/src/retrieval/pagerank.ts +40 -0
- package/src/retrieval/query-classifier.ts +106 -0
- package/src/retrieval/reranker.ts +156 -0
- package/src/retrieval/search.ts +236 -0
- package/src/retrieval/throttle.ts +102 -0
- package/src/retrieval/vector-search.ts +203 -0
- package/src/retrieval/workspace-search.ts +130 -0
- package/src/sandbox/context-mode.ts +285 -0
- package/src/sandbox/credential-pass.ts +55 -0
- package/src/sandbox/executor.ts +235 -0
- package/src/security/pattern-detector.ts +127 -0
- package/src/security/rule-of-two.ts +50 -0
- package/src/security/sanitize.ts +46 -0
- package/src/security/semantic-consistency.ts +93 -0
- package/src/security/staging-promoter.ts +154 -0
- package/src/shared/config.ts +302 -0
- package/src/shared/diagnostics.ts +210 -0
- package/src/shared/errors.ts +48 -0
- package/src/shared/git-utils.ts +143 -0
- package/src/shared/llm-client.ts +120 -0
- package/src/shared/logger.ts +99 -0
- package/src/shared/types.ts +79 -0
- package/src/sync/client.ts +43 -0
- package/src/sync/conflict.ts +106 -0
- package/src/sync/dedup.ts +183 -0
- package/src/sync/hlc.ts +117 -0
- package/src/sync/keychain.ts +144 -0
- package/src/sync/pull.ts +232 -0
- package/src/sync/push.ts +131 -0
- package/src/types/chokidar.d.ts +23 -0
- package/src/visualization/graph-renderer.ts +312 -0
- package/src/visualization/subgraph-extract.ts +208 -0
- package/src/visualization/views/community-clusters.ts +246 -0
- package/src/visualization/views/dependency-map.ts +189 -0
- package/src/visualization/views/graph-explorer.ts +364 -0
- package/src/visualization/views/timeline.ts +247 -0
- package/src/workspace/api-contracts.ts +226 -0
- package/src/workspace/cross-repo.ts +61 -0
- package/src/workspace/detector.ts +190 -0
- package/src/workspace/manifest.ts +141 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
// Module: templates — .sia/templates/ loader and validator for knowledge authoring
|
|
2
|
+
|
|
3
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
4
|
+
import { basename, join } from "node:path";
|
|
5
|
+
|
|
6
|
+
export interface TemplateField {
|
|
7
|
+
description: string;
|
|
8
|
+
required: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface KnowledgeTemplate {
|
|
12
|
+
name: string;
|
|
13
|
+
kind: string;
|
|
14
|
+
fields: Record<string, TemplateField>;
|
|
15
|
+
tagsPrefix: string[];
|
|
16
|
+
autoRelate: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Load all templates from a .sia/templates/ directory.
|
|
21
|
+
* Templates are YAML files defining structured fields for knowledge entry.
|
|
22
|
+
*
|
|
23
|
+
* Returns a map of template name -> template definition.
|
|
24
|
+
*/
|
|
25
|
+
export function loadTemplates(siaDir: string): Map<string, KnowledgeTemplate> {
|
|
26
|
+
const templates = new Map<string, KnowledgeTemplate>();
|
|
27
|
+
const templatesDir = join(siaDir, "templates");
|
|
28
|
+
|
|
29
|
+
if (!existsSync(templatesDir)) {
|
|
30
|
+
return templates;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let entries: string[];
|
|
34
|
+
try {
|
|
35
|
+
entries = readdirSync(templatesDir);
|
|
36
|
+
} catch {
|
|
37
|
+
return templates;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (const entry of entries) {
|
|
41
|
+
if (!entry.endsWith(".yaml") && !entry.endsWith(".yml")) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const filePath = join(templatesDir, entry);
|
|
46
|
+
let content: string;
|
|
47
|
+
try {
|
|
48
|
+
content = readFileSync(filePath, "utf-8");
|
|
49
|
+
} catch {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const parsed = parseSimpleYaml(content);
|
|
54
|
+
const name = basename(entry).replace(/\.ya?ml$/, "");
|
|
55
|
+
|
|
56
|
+
const rawFields = parsed.fields as Record<string, Record<string, unknown>> | undefined;
|
|
57
|
+
const fields: Record<string, TemplateField> = {};
|
|
58
|
+
|
|
59
|
+
if (rawFields && typeof rawFields === "object") {
|
|
60
|
+
for (const [fieldName, fieldDef] of Object.entries(rawFields)) {
|
|
61
|
+
if (fieldDef && typeof fieldDef === "object") {
|
|
62
|
+
fields[fieldName] = {
|
|
63
|
+
description: String(fieldDef.description ?? ""),
|
|
64
|
+
required: fieldDef.required === true,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const rawTagsPrefix = parsed.tags_prefix;
|
|
71
|
+
let tagsPrefix: string[] = [];
|
|
72
|
+
if (Array.isArray(rawTagsPrefix)) {
|
|
73
|
+
tagsPrefix = rawTagsPrefix.map(String);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const template: KnowledgeTemplate = {
|
|
77
|
+
name,
|
|
78
|
+
kind: String(parsed.kind ?? ""),
|
|
79
|
+
fields,
|
|
80
|
+
tagsPrefix,
|
|
81
|
+
autoRelate: parsed.auto_relate === true,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
templates.set(name, template);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return templates;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Validate properties against a template's required fields.
|
|
92
|
+
* Returns an array of error messages (empty if valid).
|
|
93
|
+
*/
|
|
94
|
+
export function validateTemplate(
|
|
95
|
+
template: KnowledgeTemplate,
|
|
96
|
+
properties: Record<string, unknown>,
|
|
97
|
+
): string[] {
|
|
98
|
+
const errors: string[] = [];
|
|
99
|
+
|
|
100
|
+
for (const [fieldName, field] of Object.entries(template.fields)) {
|
|
101
|
+
if (!field.required) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const value = properties[fieldName];
|
|
106
|
+
if (value === undefined || value === null || value === "") {
|
|
107
|
+
errors.push(`Missing required field "${fieldName}": ${field.description}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return errors;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get a specific template by name.
|
|
116
|
+
* Returns undefined if not found.
|
|
117
|
+
*/
|
|
118
|
+
export function getTemplate(
|
|
119
|
+
templates: Map<string, KnowledgeTemplate>,
|
|
120
|
+
name: string,
|
|
121
|
+
): KnowledgeTemplate | undefined {
|
|
122
|
+
return templates.get(name);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Simple YAML parser for template files.
|
|
127
|
+
* Handles the subset of YAML used by templates (flat and one-level nested objects).
|
|
128
|
+
*
|
|
129
|
+
* Supported syntax:
|
|
130
|
+
* - Top-level `key: value` pairs
|
|
131
|
+
* - Nested objects (2-space or tab indented)
|
|
132
|
+
* - Inline arrays `[item1, item2]`
|
|
133
|
+
* - Boolean values (true/false)
|
|
134
|
+
* - Quoted and unquoted string values
|
|
135
|
+
*/
|
|
136
|
+
function parseSimpleYaml(content: string): Record<string, unknown> {
|
|
137
|
+
const result: Record<string, unknown> = {};
|
|
138
|
+
const lines = content.split("\n");
|
|
139
|
+
|
|
140
|
+
let i = 0;
|
|
141
|
+
while (i < lines.length) {
|
|
142
|
+
const line = lines[i] as string;
|
|
143
|
+
|
|
144
|
+
// Skip empty lines and comments
|
|
145
|
+
if (line.trim() === "" || line.trim().startsWith("#")) {
|
|
146
|
+
i++;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Top-level key: must start at column 0 (no indentation)
|
|
151
|
+
if (line[0] !== " " && line[0] !== "\t") {
|
|
152
|
+
const colonIdx = line.indexOf(":");
|
|
153
|
+
if (colonIdx === -1) {
|
|
154
|
+
i++;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const key = line.slice(0, colonIdx).trim();
|
|
159
|
+
const rawValue = line.slice(colonIdx + 1).trim();
|
|
160
|
+
|
|
161
|
+
if (rawValue === "") {
|
|
162
|
+
// This is a nested object — collect indented lines
|
|
163
|
+
const nested: Record<string, unknown> = {};
|
|
164
|
+
i++;
|
|
165
|
+
const nestedResult = parseNestedBlock(lines, i);
|
|
166
|
+
Object.assign(nested, nestedResult.data);
|
|
167
|
+
i = nestedResult.nextIndex;
|
|
168
|
+
result[key] = nested;
|
|
169
|
+
} else {
|
|
170
|
+
result[key] = parseValue(rawValue);
|
|
171
|
+
i++;
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
// Indented line at top level — skip (shouldn't happen in well-formed input)
|
|
175
|
+
i++;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Parse a block of indented lines as a nested object.
|
|
184
|
+
* Handles one-level nesting (fields with sub-properties).
|
|
185
|
+
*/
|
|
186
|
+
function parseNestedBlock(
|
|
187
|
+
lines: string[],
|
|
188
|
+
startIdx: number,
|
|
189
|
+
): { data: Record<string, unknown>; nextIndex: number } {
|
|
190
|
+
const data: Record<string, unknown> = {};
|
|
191
|
+
let i = startIdx;
|
|
192
|
+
|
|
193
|
+
while (i < lines.length) {
|
|
194
|
+
const line = lines[i] as string;
|
|
195
|
+
|
|
196
|
+
// Empty line — continue (might be within the block)
|
|
197
|
+
if (line.trim() === "" || line.trim().startsWith("#")) {
|
|
198
|
+
i++;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Check if this line is indented (part of the block)
|
|
203
|
+
if (line[0] !== " " && line[0] !== "\t") {
|
|
204
|
+
// Back at top level — stop
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const indent = getIndentLevel(line);
|
|
209
|
+
const trimmed = line.trim();
|
|
210
|
+
|
|
211
|
+
const colonIdx = trimmed.indexOf(":");
|
|
212
|
+
if (colonIdx === -1) {
|
|
213
|
+
i++;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const key = trimmed.slice(0, colonIdx).trim();
|
|
218
|
+
const rawValue = trimmed.slice(colonIdx + 1).trim();
|
|
219
|
+
|
|
220
|
+
if (rawValue === "") {
|
|
221
|
+
// Sub-nested object (second level)
|
|
222
|
+
const subNested: Record<string, unknown> = {};
|
|
223
|
+
i++;
|
|
224
|
+
|
|
225
|
+
while (i < lines.length) {
|
|
226
|
+
const subLine = lines[i] as string;
|
|
227
|
+
if (subLine.trim() === "" || subLine.trim().startsWith("#")) {
|
|
228
|
+
i++;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const subIndent = getIndentLevel(subLine);
|
|
233
|
+
if (subIndent <= indent) {
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const subTrimmed = subLine.trim();
|
|
238
|
+
const subColonIdx = subTrimmed.indexOf(":");
|
|
239
|
+
if (subColonIdx !== -1) {
|
|
240
|
+
const subKey = subTrimmed.slice(0, subColonIdx).trim();
|
|
241
|
+
const subRawValue = subTrimmed.slice(subColonIdx + 1).trim();
|
|
242
|
+
subNested[subKey] = parseValue(subRawValue);
|
|
243
|
+
}
|
|
244
|
+
i++;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
data[key] = subNested;
|
|
248
|
+
} else {
|
|
249
|
+
data[key] = parseValue(rawValue);
|
|
250
|
+
i++;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return { data, nextIndex: i };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** Determine the indentation level of a line (number of leading spaces/tabs). */
|
|
258
|
+
function getIndentLevel(line: string): number {
|
|
259
|
+
let count = 0;
|
|
260
|
+
for (const ch of line) {
|
|
261
|
+
if (ch === " ") {
|
|
262
|
+
count++;
|
|
263
|
+
} else if (ch === "\t") {
|
|
264
|
+
count += 2;
|
|
265
|
+
} else {
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return count;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Parse a scalar or inline-array YAML value. */
|
|
273
|
+
function parseValue(raw: string): unknown {
|
|
274
|
+
// Remove trailing comments
|
|
275
|
+
const commentIdx = raw.indexOf(" #");
|
|
276
|
+
const cleaned = commentIdx !== -1 ? raw.slice(0, commentIdx).trim() : raw;
|
|
277
|
+
|
|
278
|
+
// Boolean
|
|
279
|
+
if (cleaned === "true") return true;
|
|
280
|
+
if (cleaned === "false") return false;
|
|
281
|
+
|
|
282
|
+
// Null
|
|
283
|
+
if (cleaned === "null" || cleaned === "~") return null;
|
|
284
|
+
|
|
285
|
+
// Inline array: [item1, item2]
|
|
286
|
+
if (cleaned.startsWith("[") && cleaned.endsWith("]")) {
|
|
287
|
+
const inner = cleaned.slice(1, -1).trim();
|
|
288
|
+
if (inner === "") return [];
|
|
289
|
+
return inner.split(",").map((item) => parseValue(item.trim()));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Quoted string
|
|
293
|
+
if (
|
|
294
|
+
(cleaned.startsWith('"') && cleaned.endsWith('"')) ||
|
|
295
|
+
(cleaned.startsWith("'") && cleaned.endsWith("'"))
|
|
296
|
+
) {
|
|
297
|
+
return cleaned.slice(1, -1);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Number
|
|
301
|
+
if (/^-?\d+(\.\d+)?$/.test(cleaned)) {
|
|
302
|
+
return Number(cleaned);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Plain string
|
|
306
|
+
return cleaned;
|
|
307
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ProviderConfig } from "@/llm/provider-registry";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Adapter that resolves a ProviderConfig to an actual AI SDK model
|
|
5
|
+
* and executes text generation.
|
|
6
|
+
*
|
|
7
|
+
* Falls back gracefully if the AI SDK or provider packages aren't available.
|
|
8
|
+
*/
|
|
9
|
+
export class AiSdkAdapter {
|
|
10
|
+
private config: ProviderConfig;
|
|
11
|
+
|
|
12
|
+
constructor(config: ProviderConfig) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async generate(prompt: string): Promise<string> {
|
|
17
|
+
const { generateText } = await import("ai");
|
|
18
|
+
const model = await this.resolveModel();
|
|
19
|
+
const result = await generateText({ model, prompt });
|
|
20
|
+
return result.text;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private async resolveModel() {
|
|
24
|
+
switch (this.config.provider) {
|
|
25
|
+
case "anthropic": {
|
|
26
|
+
const { anthropic } = await import("@ai-sdk/anthropic");
|
|
27
|
+
return anthropic(this.config.model);
|
|
28
|
+
}
|
|
29
|
+
case "openai": {
|
|
30
|
+
const { openai } = await import("@ai-sdk/openai");
|
|
31
|
+
return openai(this.config.model);
|
|
32
|
+
}
|
|
33
|
+
default:
|
|
34
|
+
throw new Error(`Unsupported AI SDK provider: ${this.config.provider}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create an adapter from a ProviderConfig, or null if the provider is unsupported.
|
|
41
|
+
*/
|
|
42
|
+
export function createAdapter(config: ProviderConfig): AiSdkAdapter | null {
|
|
43
|
+
const supported = ["anthropic", "openai"];
|
|
44
|
+
if (!supported.includes(config.provider)) return null;
|
|
45
|
+
return new AiSdkAdapter(config);
|
|
46
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import type { CaptureMode, OperationRole, ProviderConfig } from "./provider-registry";
|
|
3
|
+
|
|
4
|
+
/** Full LLM configuration for Sia. */
|
|
5
|
+
export interface SiaLlmConfig {
|
|
6
|
+
capture: {
|
|
7
|
+
mode: CaptureMode;
|
|
8
|
+
hookPort: number;
|
|
9
|
+
};
|
|
10
|
+
providers: Record<OperationRole, ProviderConfig>;
|
|
11
|
+
fallback: {
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
chain: string[];
|
|
14
|
+
maxRetries: number;
|
|
15
|
+
};
|
|
16
|
+
costTracking: {
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
budgetPerDay: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Get the built-in default config (hooks mode, Anthropic for summarize/validate). */
|
|
23
|
+
export function getDefaultLlmConfig(): SiaLlmConfig {
|
|
24
|
+
return {
|
|
25
|
+
capture: {
|
|
26
|
+
mode: "hooks",
|
|
27
|
+
hookPort: 4521,
|
|
28
|
+
},
|
|
29
|
+
providers: {
|
|
30
|
+
summarize: { provider: "anthropic", model: "claude-sonnet-4" },
|
|
31
|
+
validate: { provider: "ollama", model: "qwen2.5-coder:7b" },
|
|
32
|
+
extract: { provider: "anthropic", model: "claude-haiku-4-5" },
|
|
33
|
+
consolidate: { provider: "anthropic", model: "claude-haiku-4-5" },
|
|
34
|
+
},
|
|
35
|
+
fallback: {
|
|
36
|
+
enabled: true,
|
|
37
|
+
chain: ["anthropic", "openai", "ollama"],
|
|
38
|
+
maxRetries: 3,
|
|
39
|
+
},
|
|
40
|
+
costTracking: {
|
|
41
|
+
enabled: true,
|
|
42
|
+
budgetPerDay: 5.0,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Load config from a YAML-like config file path, or return defaults.
|
|
49
|
+
* Currently supports a simple JSON config; full YAML support can be added later.
|
|
50
|
+
*/
|
|
51
|
+
export function loadLlmConfig(configPath?: string): SiaLlmConfig {
|
|
52
|
+
const defaults = getDefaultLlmConfig();
|
|
53
|
+
|
|
54
|
+
if (!configPath || !existsSync(configPath)) {
|
|
55
|
+
return defaults;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
60
|
+
const parsed = JSON.parse(raw) as Partial<SiaLlmConfig>;
|
|
61
|
+
return mergeConfig(defaults, parsed);
|
|
62
|
+
} catch {
|
|
63
|
+
// If the file can't be parsed, return defaults
|
|
64
|
+
return defaults;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Deep-merge a partial config onto the defaults. */
|
|
69
|
+
function mergeConfig(defaults: SiaLlmConfig, partial: Partial<SiaLlmConfig>): SiaLlmConfig {
|
|
70
|
+
return {
|
|
71
|
+
capture: {
|
|
72
|
+
...defaults.capture,
|
|
73
|
+
...(partial.capture ?? {}),
|
|
74
|
+
},
|
|
75
|
+
providers: {
|
|
76
|
+
...defaults.providers,
|
|
77
|
+
...(partial.providers ?? {}),
|
|
78
|
+
},
|
|
79
|
+
fallback: {
|
|
80
|
+
...defaults.fallback,
|
|
81
|
+
...(partial.fallback ?? {}),
|
|
82
|
+
},
|
|
83
|
+
costTracking: {
|
|
84
|
+
...defaults.costTracking,
|
|
85
|
+
...(partial.costTracking ?? {}),
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { OperationRole } from "./provider-registry";
|
|
2
|
+
|
|
3
|
+
/** A single LLM API call record. */
|
|
4
|
+
export interface CostEntry {
|
|
5
|
+
timestamp: number;
|
|
6
|
+
provider: string;
|
|
7
|
+
model: string;
|
|
8
|
+
role: OperationRole;
|
|
9
|
+
input_tokens: number;
|
|
10
|
+
output_tokens: number;
|
|
11
|
+
estimated_cost: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Per-call cost tracking with optional daily budget enforcement.
|
|
16
|
+
* Entries are kept in-memory; a future persistence layer can flush to the meta database.
|
|
17
|
+
*/
|
|
18
|
+
export class CostTracker {
|
|
19
|
+
private entries: CostEntry[] = [];
|
|
20
|
+
private readonly dailyBudget: number | undefined;
|
|
21
|
+
|
|
22
|
+
constructor(dailyBudget?: number) {
|
|
23
|
+
this.dailyBudget = dailyBudget;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Log a call and attach a timestamp. */
|
|
27
|
+
logCall(entry: Omit<CostEntry, "timestamp">): void {
|
|
28
|
+
this.entries.push({ ...entry, timestamp: Date.now() });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Get today's total spend (UTC day boundary). */
|
|
32
|
+
getTodaySpend(): number {
|
|
33
|
+
const todayStart = startOfDayUtc(Date.now());
|
|
34
|
+
return this.entries
|
|
35
|
+
.filter((e) => e.timestamp >= todayStart)
|
|
36
|
+
.reduce((sum, e) => sum + e.estimated_cost, 0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Check if the daily budget has been exceeded. */
|
|
40
|
+
isBudgetExceeded(): boolean {
|
|
41
|
+
if (this.dailyBudget === undefined) return false;
|
|
42
|
+
return this.getTodaySpend() >= this.dailyBudget;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Get all entries, optionally filtered to those after a timestamp. */
|
|
46
|
+
getEntries(since?: number): CostEntry[] {
|
|
47
|
+
if (since === undefined) return [...this.entries];
|
|
48
|
+
return this.entries.filter((e) => e.timestamp >= since);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Estimate cost in USD based on provider, model, and token counts.
|
|
53
|
+
* Uses approximate per-million-token pricing.
|
|
54
|
+
*/
|
|
55
|
+
static estimateCost(
|
|
56
|
+
provider: string,
|
|
57
|
+
model: string,
|
|
58
|
+
inputTokens: number,
|
|
59
|
+
outputTokens: number,
|
|
60
|
+
): number {
|
|
61
|
+
// Local models are free
|
|
62
|
+
if (provider === "ollama" || provider === "local") {
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const rates = getCostRates(provider, model);
|
|
67
|
+
const inputCost = (inputTokens / 1_000_000) * rates.inputPerMillion;
|
|
68
|
+
const outputCost = (outputTokens / 1_000_000) * rates.outputPerMillion;
|
|
69
|
+
return inputCost + outputCost;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Returns UTC midnight for the given timestamp. */
|
|
74
|
+
function startOfDayUtc(ts: number): number {
|
|
75
|
+
const d = new Date(ts);
|
|
76
|
+
d.setUTCHours(0, 0, 0, 0);
|
|
77
|
+
return d.getTime();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface CostRates {
|
|
81
|
+
inputPerMillion: number;
|
|
82
|
+
outputPerMillion: number;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Approximate per-million-token pricing. */
|
|
86
|
+
function getCostRates(provider: string, model: string): CostRates {
|
|
87
|
+
if (provider === "anthropic") {
|
|
88
|
+
if (model.includes("haiku")) {
|
|
89
|
+
return { inputPerMillion: 0.8, outputPerMillion: 4.0 };
|
|
90
|
+
}
|
|
91
|
+
if (model.includes("sonnet")) {
|
|
92
|
+
return { inputPerMillion: 3.0, outputPerMillion: 15.0 };
|
|
93
|
+
}
|
|
94
|
+
if (model.includes("opus")) {
|
|
95
|
+
return { inputPerMillion: 15.0, outputPerMillion: 75.0 };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (provider === "openai") {
|
|
100
|
+
if (model.includes("gpt-4o-mini")) {
|
|
101
|
+
return { inputPerMillion: 0.15, outputPerMillion: 0.6 };
|
|
102
|
+
}
|
|
103
|
+
if (model.includes("gpt-4o")) {
|
|
104
|
+
return { inputPerMillion: 2.5, outputPerMillion: 10.0 };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Fallback: generic cloud pricing
|
|
109
|
+
return { inputPerMillion: 1.0, outputPerMillion: 5.0 };
|
|
110
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/llm/prompts/extraction.ts — Batch extraction prompt for api/hybrid capture mode
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extraction prompt for api/hybrid capture mode.
|
|
5
|
+
* Used when hooks are unavailable (non-Claude-Code agents, CLI batch operations).
|
|
6
|
+
*
|
|
7
|
+
* Uses XML-delimited sections for cross-provider compatibility.
|
|
8
|
+
* Works across Claude, GPT, Gemini, and Ollama without modification.
|
|
9
|
+
*/
|
|
10
|
+
export function batchExtractionPrompt(
|
|
11
|
+
transcriptChunk: string,
|
|
12
|
+
context?: Array<{ name: string; type: string }>,
|
|
13
|
+
): { system: string; user: string } {
|
|
14
|
+
const system = [
|
|
15
|
+
"<instructions>",
|
|
16
|
+
"Extract structured knowledge facts from the following development session transcript.",
|
|
17
|
+
"",
|
|
18
|
+
"For each fact, identify:",
|
|
19
|
+
"- Entity: the named concept, file, function, decision, or convention",
|
|
20
|
+
"- Type: Technology | File | Function | Decision | Convention | Bug | Pattern | Other",
|
|
21
|
+
"- Summary: a concise description of what is known about this entity",
|
|
22
|
+
"- Relationships: connections to other entities (optional)",
|
|
23
|
+
"",
|
|
24
|
+
"Guidelines:",
|
|
25
|
+
"- Extract only factual, reusable knowledge — skip transient reasoning steps.",
|
|
26
|
+
"- Focus on architectural decisions, naming conventions, API contracts, and technical patterns.",
|
|
27
|
+
"- Prefer specificity: 'uses bun:sqlite via SiaDb adapter' over 'uses a database'.",
|
|
28
|
+
"- Output valid JSON: { entities: Array<{ name: string, type: string, summary: string, related?: string[] }> }",
|
|
29
|
+
"</instructions>",
|
|
30
|
+
].join("\n");
|
|
31
|
+
|
|
32
|
+
const contextBlock = buildContextBlock(context);
|
|
33
|
+
const user = [
|
|
34
|
+
contextBlock,
|
|
35
|
+
"<transcript>",
|
|
36
|
+
transcriptChunk,
|
|
37
|
+
"</transcript>",
|
|
38
|
+
"",
|
|
39
|
+
"Extract all knowledge entities from the transcript above. Return JSON with an `entities` array.",
|
|
40
|
+
]
|
|
41
|
+
.filter(Boolean)
|
|
42
|
+
.join("\n");
|
|
43
|
+
|
|
44
|
+
return { system, user };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Build the context block if prior entities are provided. */
|
|
48
|
+
function buildContextBlock(context?: Array<{ name: string; type: string }>): string {
|
|
49
|
+
if (!context || context.length === 0) return "";
|
|
50
|
+
|
|
51
|
+
const lines = context.map((e) => `- ${e.name} (${e.type})`).join("\n");
|
|
52
|
+
return ["<context>", "Known entities (avoid duplicating these):", lines, "</context>", ""].join(
|
|
53
|
+
"\n",
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt templates for community summarization (always-active LLM role).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Build a system + user prompt pair for summarizing a community of entities.
|
|
7
|
+
* Used by the RAPTOR summarization pipeline and community detection.
|
|
8
|
+
*/
|
|
9
|
+
export function communitySummaryPrompt(
|
|
10
|
+
entityNames: string[],
|
|
11
|
+
entitySummaries: string[],
|
|
12
|
+
): { system: string; user: string } {
|
|
13
|
+
const system = [
|
|
14
|
+
"You are Sia, a knowledge-graph memory system for software projects.",
|
|
15
|
+
"Your task is to produce a concise, coherent summary of a cluster of related knowledge entities.",
|
|
16
|
+
"",
|
|
17
|
+
"Guidelines:",
|
|
18
|
+
"- Write a single paragraph (2-5 sentences) that captures the common theme.",
|
|
19
|
+
"- Mention the most important entities by name.",
|
|
20
|
+
"- Focus on actionable information: decisions, conventions, patterns.",
|
|
21
|
+
"- Do NOT list entities — synthesize them into a narrative.",
|
|
22
|
+
"- Output valid JSON matching: { summary: string, key_entities: string[], confidence: number }",
|
|
23
|
+
].join("\n");
|
|
24
|
+
|
|
25
|
+
const entityBlock = entityNames
|
|
26
|
+
.map((name, i) => `### ${name}\n${entitySummaries[i] ?? "(no summary)"}`)
|
|
27
|
+
.join("\n\n");
|
|
28
|
+
|
|
29
|
+
const user = [
|
|
30
|
+
"Summarize the following community of knowledge entities:\n",
|
|
31
|
+
entityBlock,
|
|
32
|
+
"\nReturn JSON with: summary (coherent paragraph), key_entities (up to 10 most important names), confidence (0-1).",
|
|
33
|
+
].join("\n");
|
|
34
|
+
|
|
35
|
+
return { system, user };
|
|
36
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt templates for fact validation (always-active LLM role).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Build a system + user prompt pair for validating whether a stored fact
|
|
7
|
+
* is still correct given current code context.
|
|
8
|
+
*/
|
|
9
|
+
export function factValidationPrompt(
|
|
10
|
+
factContent: string,
|
|
11
|
+
codeContext: string,
|
|
12
|
+
): { system: string; user: string } {
|
|
13
|
+
const system = [
|
|
14
|
+
"You are Sia, a knowledge-graph memory system for software projects.",
|
|
15
|
+
"Your task is to validate whether a previously stored fact is still accurate",
|
|
16
|
+
"given the current state of the codebase.",
|
|
17
|
+
"",
|
|
18
|
+
"Guidelines:",
|
|
19
|
+
"- Compare the fact against the provided code context.",
|
|
20
|
+
"- If the code confirms the fact, respond with action: 'confirm'.",
|
|
21
|
+
"- If the code contradicts the fact, respond with action: 'invalidate'.",
|
|
22
|
+
"- If you are unsure, respond with action: 'flag_for_review'.",
|
|
23
|
+
"- Output valid JSON matching: { is_valid: boolean, confidence: number, reasoning: string, action: 'confirm' | 'invalidate' | 'flag_for_review' }",
|
|
24
|
+
].join("\n");
|
|
25
|
+
|
|
26
|
+
const user = [
|
|
27
|
+
"## Stored Fact",
|
|
28
|
+
factContent,
|
|
29
|
+
"",
|
|
30
|
+
"## Current Code Context",
|
|
31
|
+
codeContext,
|
|
32
|
+
"",
|
|
33
|
+
"Is this fact still valid? Return JSON with: is_valid, confidence (0-1), reasoning, action.",
|
|
34
|
+
].join("\n");
|
|
35
|
+
|
|
36
|
+
return { system, user };
|
|
37
|
+
}
|