create-hq 5.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/dist/deps.d.ts +4 -0
- package/dist/deps.d.ts.map +1 -0
- package/dist/deps.js +65 -0
- package/dist/deps.js.map +1 -0
- package/dist/git.d.ts +3 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +19 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/scaffold.d.ts +8 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +130 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/ui.d.ts +7 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +36 -0
- package/dist/ui.js.map +1 -0
- package/package.json +41 -0
- package/template/.claude/CLAUDE.md +202 -0
- package/template/.claude/commands/checkpoint.md +127 -0
- package/template/.claude/commands/cleanup.md +307 -0
- package/template/.claude/commands/execute-task.md +440 -0
- package/template/.claude/commands/exit-plan.md +41 -0
- package/template/.claude/commands/handoff.md +97 -0
- package/template/.claude/commands/learn.md +218 -0
- package/template/.claude/commands/metrics.md +118 -0
- package/template/.claude/commands/newworker.md +162 -0
- package/template/.claude/commands/nexttask.md +67 -0
- package/template/.claude/commands/prd.md +238 -0
- package/template/.claude/commands/reanchor.md +51 -0
- package/template/.claude/commands/remember.md +126 -0
- package/template/.claude/commands/run-project.md +348 -0
- package/template/.claude/commands/run.md +110 -0
- package/template/.claude/commands/search-reindex.md +62 -0
- package/template/.claude/commands/search.md +100 -0
- package/template/.claude/commands/setup.md +381 -0
- package/template/.claude/scripts/pure-ralph-loop.ps1 +312 -0
- package/template/.claude/scripts/pure-ralph-loop.sh +859 -0
- package/template/CHANGELOG.md +220 -0
- package/template/LICENSE +21 -0
- package/template/MIGRATION.md +259 -0
- package/template/README.md +368 -0
- package/template/data/journal/.gitkeep +0 -0
- package/template/docs/images/ascii-banner-options.md +122 -0
- package/template/docs/images/hq-banner.svg +105 -0
- package/template/knowledge/Ralph/01-overview.md +71 -0
- package/template/knowledge/Ralph/02-core-concepts.md +114 -0
- package/template/knowledge/Ralph/03-how-ralph-works.md +184 -0
- package/template/knowledge/Ralph/04-back-pressure.md +222 -0
- package/template/knowledge/Ralph/05-specifications.md +210 -0
- package/template/knowledge/Ralph/06-agents-md.md +222 -0
- package/template/knowledge/Ralph/07-implementation.md +316 -0
- package/template/knowledge/Ralph/08-economics.md +182 -0
- package/template/knowledge/Ralph/09-resources.md +145 -0
- package/template/knowledge/Ralph/10-claude-code-workflow.md +212 -0
- package/template/knowledge/Ralph/11-team-training-guide.md +383 -0
- package/template/knowledge/Ralph/README.md +40 -0
- package/template/knowledge/ai-security-framework/CONTRIBUTING.md +139 -0
- package/template/knowledge/ai-security-framework/GLOSSARY.md +176 -0
- package/template/knowledge/ai-security-framework/LICENSE +21 -0
- package/template/knowledge/ai-security-framework/QUICK-START.md +172 -0
- package/template/knowledge/ai-security-framework/README.md +232 -0
- package/template/knowledge/ai-security-framework/checklists/browser-security.md +301 -0
- package/template/knowledge/ai-security-framework/checklists/credential-isolation.md +322 -0
- package/template/knowledge/ai-security-framework/checklists/incident-response.md +288 -0
- package/template/knowledge/ai-security-framework/checklists/pre-flight.md +249 -0
- package/template/knowledge/ai-security-framework/checklists/weekly-audit.md +159 -0
- package/template/knowledge/ai-security-framework/configs/audit-logging.md +372 -0
- package/template/knowledge/ai-security-framework/configs/kill-switches.md +354 -0
- package/template/knowledge/ai-security-framework/docs/01-core-principles.md +256 -0
- package/template/knowledge/ai-security-framework/docs/02-threat-landscape.md +326 -0
- package/template/knowledge/ai-security-framework/docs/03-security-posture.md +250 -0
- package/template/knowledge/ai-security-framework/templates/agents-security.md +233 -0
- package/template/knowledge/design-styles/README.md +42 -0
- package/template/knowledge/design-styles/american-industrial.md +136 -0
- package/template/knowledge/design-styles/ethereal-abstract.md +133 -0
- package/template/knowledge/design-styles/liminal-portal.md +111 -0
- package/template/knowledge/design-styles/swipes/american-industrial/G-3m4YPW0AADdu2.jpeg +0 -0
- package/template/knowledge/design-styles/swipes/american-industrial/G-JJlt5WwAABK3K.png +0 -0
- package/template/knowledge/design-styles/swipes/american-industrial/G-JJmj5W0AEbJ-7.png +0 -0
- package/template/knowledge/design-styles/swipes/american-industrial/G59fgNuXkAAKLJQ (1).jpeg +0 -0
- package/template/knowledge/design-styles/swipes/american-industrial/G59fgNuXkAAKLJQ.jpeg +0 -0
- package/template/knowledge/design-styles/swipes/american-industrial/G7fVkn3WEAAM-ST.jpeg +0 -0
- package/template/knowledge/design-styles/swipes/american-industrial/G8ECO5JWEAIksyn.png +0 -0
- package/template/knowledge/design-styles/swipes/american-industrial/G9-3GQSWoAA8eqZ.png +0 -0
- package/template/knowledge/design-styles/swipes/american-industrial/G9xEOqrXkAEZRcs.png +0 -0
- package/template/knowledge/design-styles/swipes/american-industrial/G_MVeJrXQAA8sx4.jpeg +0 -0
- package/template/knowledge/design-styles/swipes/american-industrial/G_RSkmGXkAAgAVZ.png +0 -0
- package/template/knowledge/design-styles/swipes/american-industrial/README.md +31 -0
- package/template/knowledge/design-styles/swipes/american-industrial/qyqtg7Dq.png +0 -0
- package/template/knowledge/dev-team/README.md +35 -0
- package/template/knowledge/dev-team/patterns/README.md +34 -0
- package/template/knowledge/dev-team/patterns/frontend/react-best-practices.md +178 -0
- package/template/knowledge/dev-team/troubleshooting/README.md +31 -0
- package/template/knowledge/dev-team/workflows/README.md +49 -0
- package/template/knowledge/hq/checkpoint-schema.json +51 -0
- package/template/knowledge/hq/index-md-spec.md +74 -0
- package/template/knowledge/hq/thread-schema.md +153 -0
- package/template/knowledge/hq-core/checkpoint-schema.json +51 -0
- package/template/knowledge/hq-core/index-md-spec.md +74 -0
- package/template/knowledge/hq-core/thread-schema.md +153 -0
- package/template/knowledge/loom/README.md +51 -0
- package/template/knowledge/loom/architecture.md +125 -0
- package/template/knowledge/loom/code-style.md +169 -0
- package/template/knowledge/loom/llm-proxy.md +132 -0
- package/template/knowledge/loom/state-machine.md +131 -0
- package/template/knowledge/loom/thread-system.md +117 -0
- package/template/knowledge/loom/tools.md +94 -0
- package/template/knowledge/loom/weaver.md +96 -0
- package/template/knowledge/loom/web-frontend.md +131 -0
- package/template/knowledge/projects/README.md +72 -0
- package/template/knowledge/projects/templates/README.template.md +28 -0
- package/template/knowledge/workers/README.md +195 -0
- package/template/knowledge/workers/ralph-loop-pattern.md +157 -0
- package/template/knowledge/workers/skill-schema.md +182 -0
- package/template/knowledge/workers/state-machine.md +102 -0
- package/template/knowledge/workers/templates/base-worker.yaml +73 -0
- package/template/knowledge/workers/templates/code-worker.yaml +85 -0
- package/template/knowledge/workers/templates/skill.yaml +49 -0
- package/template/knowledge/workers/templates/social-worker.yaml +70 -0
- package/template/modules/examples/full-manifest.yaml +92 -0
- package/template/modules/examples/minimal.yaml +14 -0
- package/template/modules/modules.yaml +59 -0
- package/template/projects/.gitkeep +0 -0
- package/template/projects/incorporate-workers-into-pure-ralph/prd.json +88 -0
- package/template/projects/pure-ralph-branch-isolation/README.md +114 -0
- package/template/projects/pure-ralph-branch-isolation/prd.json +123 -0
- package/template/projects/purist-ralph-loop/README.md +148 -0
- package/template/projects/purist-ralph-loop/prd.json +135 -0
- package/template/projects/ralph-test/prd.json +50 -0
- package/template/prompts/pure-ralph-base.md +551 -0
- package/template/settings/.gitkeep +0 -0
- package/template/settings/pure-ralph.json +42 -0
- package/template/social-content/drafts/INDEX.md +21 -0
- package/template/social-content/drafts/linkedin/.gitkeep +1 -0
- package/template/social-content/drafts/x/.gitkeep +1 -0
- package/template/social-content/images/.gitkeep +1 -0
- package/template/starter-projects/code-worker/README.md +97 -0
- package/template/starter-projects/code-worker/prd.json +45 -0
- package/template/starter-projects/personal-assistant/README.md +42 -0
- package/template/starter-projects/personal-assistant/prd.json +43 -0
- package/template/starter-projects/social-media/README.md +60 -0
- package/template/starter-projects/social-media/prd.json +43 -0
- package/template/workers/content-brand/README.md +59 -0
- package/template/workers/content-brand/skills/messaging-alignment.md +91 -0
- package/template/workers/content-brand/skills/tone-check.md +76 -0
- package/template/workers/content-brand/skills/voice-analysis.md +68 -0
- package/template/workers/content-brand/worker.yaml +81 -0
- package/template/workers/content-legal/README.md +80 -0
- package/template/workers/content-legal/skills/claim-substantiation.md +150 -0
- package/template/workers/content-legal/skills/compliance-scan.md +123 -0
- package/template/workers/content-legal/skills/disclaimer-check.md +146 -0
- package/template/workers/content-legal/worker.yaml +118 -0
- package/template/workers/content-product/README.md +77 -0
- package/template/workers/content-product/skills/claim-verification.md +96 -0
- package/template/workers/content-product/skills/feature-accuracy.md +117 -0
- package/template/workers/content-product/skills/stats-check.md +128 -0
- package/template/workers/content-product/worker.yaml +97 -0
- package/template/workers/content-sales/README.md +70 -0
- package/template/workers/content-sales/skills/conversion-analysis.md +96 -0
- package/template/workers/content-sales/skills/cta-audit.md +107 -0
- package/template/workers/content-sales/skills/value-prop-check.md +114 -0
- package/template/workers/content-sales/worker.yaml +93 -0
- package/template/workers/content-shared/cli.ts +242 -0
- package/template/workers/content-shared/index.ts +234 -0
- package/template/workers/content-shared/lib/accuracy-analyzer.ts +661 -0
- package/template/workers/content-shared/lib/analyze.ts +370 -0
- package/template/workers/content-shared/lib/brand-analyzer.ts +526 -0
- package/template/workers/content-shared/lib/cms-integration.ts +446 -0
- package/template/workers/content-shared/lib/compliance-analyzer.ts +655 -0
- package/template/workers/content-shared/lib/conversion-analyzer.ts +555 -0
- package/template/workers/content-shared/lib/github-integration.ts +582 -0
- package/template/workers/content-shared/lib/output.ts +373 -0
- package/template/workers/content-shared/lib/parser.ts +771 -0
- package/template/workers/content-shared/lib/priority.ts +439 -0
- package/template/workers/content-shared/lib/recommendations.ts +512 -0
- package/template/workers/content-shared/lib/reporter.ts +749 -0
- package/template/workers/content-shared/lib/restructure.ts +664 -0
- package/template/workers/content-shared/lib/scorer.ts +140 -0
- package/template/workers/content-shared/lib/types.ts +227 -0
- package/template/workers/content-shared/lib/variants.ts +595 -0
- package/template/workers/content-shared/package.json +51 -0
- package/template/workers/content-shared/pnpm-lock.yaml +39 -0
- package/template/workers/content-shared/test/sample-page.json +115 -0
- package/template/workers/content-shared/tsconfig.json +20 -0
- package/template/workers/dev-team/README.md +166 -0
- package/template/workers/dev-team/_template.yaml +70 -0
- package/template/workers/dev-team/architect/package.json +27 -0
- package/template/workers/dev-team/architect/skills/api-design.md +89 -0
- package/template/workers/dev-team/architect/skills/refactor-plan.md +96 -0
- package/template/workers/dev-team/architect/skills/system-design.md +100 -0
- package/template/workers/dev-team/architect/src/index.ts +49 -0
- package/template/workers/dev-team/architect/src/mcp-server.ts +122 -0
- package/template/workers/dev-team/architect/src/skills/api-design.ts +316 -0
- package/template/workers/dev-team/architect/src/skills/refactor-plan.ts +264 -0
- package/template/workers/dev-team/architect/src/skills/system-design.ts +212 -0
- package/template/workers/dev-team/architect/tsconfig.json +19 -0
- package/template/workers/dev-team/architect/worker.yaml +128 -0
- package/template/workers/dev-team/backend-dev/package-lock.json +1252 -0
- package/template/workers/dev-team/backend-dev/package.json +27 -0
- package/template/workers/dev-team/backend-dev/skills/implement-endpoint.md +70 -0
- package/template/workers/dev-team/backend-dev/skills/implement-service.md +62 -0
- package/template/workers/dev-team/backend-dev/src/index.ts +51 -0
- package/template/workers/dev-team/backend-dev/src/mcp-server.ts +109 -0
- package/template/workers/dev-team/backend-dev/src/skills/implement-endpoint.ts +122 -0
- package/template/workers/dev-team/backend-dev/src/skills/implement-service.ts +126 -0
- package/template/workers/dev-team/backend-dev/tsconfig.json +19 -0
- package/template/workers/dev-team/backend-dev/worker.yaml +128 -0
- package/template/workers/dev-team/code-reviewer/package-lock.json +1080 -0
- package/template/workers/dev-team/code-reviewer/package.json +24 -0
- package/template/workers/dev-team/code-reviewer/skills/merge-to-production.md +61 -0
- package/template/workers/dev-team/code-reviewer/skills/merge-to-staging.md +54 -0
- package/template/workers/dev-team/code-reviewer/skills/request-changes.md +63 -0
- package/template/workers/dev-team/code-reviewer/skills/review-pr.md +77 -0
- package/template/workers/dev-team/code-reviewer/src/index.ts +56 -0
- package/template/workers/dev-team/code-reviewer/src/mcp-server.ts +101 -0
- package/template/workers/dev-team/code-reviewer/tsconfig.json +19 -0
- package/template/workers/dev-team/code-reviewer/worker.yaml +90 -0
- package/template/workers/dev-team/database-dev/package.json +22 -0
- package/template/workers/dev-team/database-dev/skills/create-schema.md +48 -0
- package/template/workers/dev-team/database-dev/src/index.ts +50 -0
- package/template/workers/dev-team/database-dev/src/mcp-server.ts +76 -0
- package/template/workers/dev-team/database-dev/tsconfig.json +18 -0
- package/template/workers/dev-team/database-dev/worker.yaml +90 -0
- package/template/workers/dev-team/frontend-dev/package.json +22 -0
- package/template/workers/dev-team/frontend-dev/skills/create-component.md +26 -0
- package/template/workers/dev-team/frontend-dev/src/index.ts +50 -0
- package/template/workers/dev-team/frontend-dev/src/mcp-server.ts +77 -0
- package/template/workers/dev-team/frontend-dev/tsconfig.json +18 -0
- package/template/workers/dev-team/frontend-dev/worker.yaml +132 -0
- package/template/workers/dev-team/infra-dev/package.json +24 -0
- package/template/workers/dev-team/infra-dev/skills/add-monitoring.md +73 -0
- package/template/workers/dev-team/infra-dev/skills/configure-deployment.md +80 -0
- package/template/workers/dev-team/infra-dev/skills/create-dockerfile.md +62 -0
- package/template/workers/dev-team/infra-dev/skills/setup-cicd.md +63 -0
- package/template/workers/dev-team/infra-dev/src/index.ts +55 -0
- package/template/workers/dev-team/infra-dev/src/mcp-server.ts +82 -0
- package/template/workers/dev-team/infra-dev/tsconfig.json +19 -0
- package/template/workers/dev-team/infra-dev/worker.yaml +92 -0
- package/template/workers/dev-team/knowledge-curator/package.json +24 -0
- package/template/workers/dev-team/knowledge-curator/skills/curate-troubleshooting.md +63 -0
- package/template/workers/dev-team/knowledge-curator/skills/process-learnings.md +61 -0
- package/template/workers/dev-team/knowledge-curator/skills/sync-documentation.md +76 -0
- package/template/workers/dev-team/knowledge-curator/skills/update-patterns.md +63 -0
- package/template/workers/dev-team/knowledge-curator/src/index.ts +53 -0
- package/template/workers/dev-team/knowledge-curator/src/mcp-server.ts +92 -0
- package/template/workers/dev-team/knowledge-curator/tsconfig.json +19 -0
- package/template/workers/dev-team/knowledge-curator/worker.yaml +80 -0
- package/template/workers/dev-team/motion-designer/package.json +22 -0
- package/template/workers/dev-team/motion-designer/skills/add-animation.md +25 -0
- package/template/workers/dev-team/motion-designer/skills/generate-image.md +36 -0
- package/template/workers/dev-team/motion-designer/src/index.ts +63 -0
- package/template/workers/dev-team/motion-designer/src/mcp-server.ts +79 -0
- package/template/workers/dev-team/motion-designer/tsconfig.json +18 -0
- package/template/workers/dev-team/motion-designer/worker.yaml +84 -0
- package/template/workers/dev-team/product-planner/queue.json +4 -0
- package/template/workers/dev-team/product-planner/worker.yaml +220 -0
- package/template/workers/dev-team/project-manager/package-lock.json +1252 -0
- package/template/workers/dev-team/project-manager/package.json +27 -0
- package/template/workers/dev-team/project-manager/skills/create-prd.md +66 -0
- package/template/workers/dev-team/project-manager/skills/next-issue.md +51 -0
- package/template/workers/dev-team/project-manager/skills/project-status.md +59 -0
- package/template/workers/dev-team/project-manager/skills/update-learnings.md +65 -0
- package/template/workers/dev-team/project-manager/src/index.ts +54 -0
- package/template/workers/dev-team/project-manager/src/mcp-server.ts +207 -0
- package/template/workers/dev-team/project-manager/src/skills/create-prd.ts +86 -0
- package/template/workers/dev-team/project-manager/src/skills/next-issue.ts +137 -0
- package/template/workers/dev-team/project-manager/src/skills/project-status.ts +131 -0
- package/template/workers/dev-team/project-manager/src/skills/update-learnings.ts +94 -0
- package/template/workers/dev-team/project-manager/tsconfig.json +19 -0
- package/template/workers/dev-team/project-manager/worker.yaml +96 -0
- package/template/workers/dev-team/qa-tester/package.json +24 -0
- package/template/workers/dev-team/qa-tester/skills/create-demo-account.md +36 -0
- package/template/workers/dev-team/qa-tester/skills/run-tests.md +36 -0
- package/template/workers/dev-team/qa-tester/skills/write-test.md +27 -0
- package/template/workers/dev-team/qa-tester/src/index.ts +61 -0
- package/template/workers/dev-team/qa-tester/src/mcp-server.ts +88 -0
- package/template/workers/dev-team/qa-tester/tsconfig.json +18 -0
- package/template/workers/dev-team/qa-tester/worker.yaml +116 -0
- package/template/workers/dev-team/task-executor/package-lock.json +1252 -0
- package/template/workers/dev-team/task-executor/package.json +27 -0
- package/template/workers/dev-team/task-executor/skills/analyze-issue.md +101 -0
- package/template/workers/dev-team/task-executor/skills/execute.md +133 -0
- package/template/workers/dev-team/task-executor/skills/report-learnings.md +106 -0
- package/template/workers/dev-team/task-executor/skills/validate-completion.md +121 -0
- package/template/workers/dev-team/task-executor/src/index.ts +54 -0
- package/template/workers/dev-team/task-executor/src/mcp-server.ts +139 -0
- package/template/workers/dev-team/task-executor/src/skills/analyze-issue.ts +219 -0
- package/template/workers/dev-team/task-executor/src/skills/execute.ts +132 -0
- package/template/workers/dev-team/task-executor/src/skills/report-learnings.ts +119 -0
- package/template/workers/dev-team/task-executor/src/skills/validate-completion.ts +142 -0
- package/template/workers/dev-team/task-executor/tsconfig.json +19 -0
- package/template/workers/dev-team/task-executor/worker.yaml +110 -0
- package/template/workers/registry.yaml +171 -0
- package/template/workers/security-scanner/README.md +73 -0
- package/template/workers/security-scanner/skills/pre-deploy-check.md +205 -0
- package/template/workers/security-scanner/worker.yaml +26 -0
- package/template/workspace/checkpoints/.gitkeep +0 -0
- package/template/workspace/content-ideas/inbox.jsonl +0 -0
- package/template/workspace/drafts/.gitkeep +0 -0
- package/template/workspace/learnings/.gitkeep +3 -0
- package/template/workspace/orchestrator/.gitkeep +0 -0
- package/template/workspace/ralph-test/COMPLETE.md +18 -0
- package/template/workspace/ralph-test/hello.txt +2 -0
- package/template/workspace/reports/.gitkeep +0 -0
- package/template/workspace/scratch/.gitkeep +0 -0
- package/template/workspace/threads/.gitkeep +3 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Code Style & Conventions
|
|
2
|
+
|
|
3
|
+
## Rust Formatting
|
|
4
|
+
|
|
5
|
+
```toml
|
|
6
|
+
# rustfmt.toml
|
|
7
|
+
hard_tabs = true
|
|
8
|
+
tab_spaces = 2
|
|
9
|
+
max_width = 100
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Error Handling
|
|
13
|
+
|
|
14
|
+
```rust
|
|
15
|
+
// Use thiserror for error enums
|
|
16
|
+
use thiserror::Error;
|
|
17
|
+
|
|
18
|
+
#[derive(Error, Debug)]
|
|
19
|
+
pub enum MyError {
|
|
20
|
+
#[error("failed to do X: {0}")]
|
|
21
|
+
SomeFailure(String),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Use anyhow for propagation
|
|
25
|
+
use anyhow::Result;
|
|
26
|
+
|
|
27
|
+
pub fn do_thing() -> Result<()> {
|
|
28
|
+
// ...
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Async
|
|
33
|
+
|
|
34
|
+
- Runtime: Tokio
|
|
35
|
+
- Trait methods: `async-trait`
|
|
36
|
+
|
|
37
|
+
```rust
|
|
38
|
+
use async_trait::async_trait;
|
|
39
|
+
|
|
40
|
+
#[async_trait]
|
|
41
|
+
pub trait MyTrait: Send + Sync {
|
|
42
|
+
async fn do_thing(&self) -> Result<()>;
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## HTTP Clients
|
|
47
|
+
|
|
48
|
+
**Never use `reqwest::Client` directly.** Use `loom-http` wrapper:
|
|
49
|
+
|
|
50
|
+
```rust
|
|
51
|
+
use loom_http::{new_client, builder};
|
|
52
|
+
|
|
53
|
+
// Consistent User-Agent and retry logic
|
|
54
|
+
let client = new_client()?;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Secrets
|
|
58
|
+
|
|
59
|
+
Use `loom-secret` for API keys, tokens, passwords:
|
|
60
|
+
|
|
61
|
+
```rust
|
|
62
|
+
use loom_secret::{Secret, SecretString};
|
|
63
|
+
|
|
64
|
+
let api_key: SecretString = SecretString::new("sk-xxx");
|
|
65
|
+
|
|
66
|
+
// Access via expose()
|
|
67
|
+
api_key.expose()
|
|
68
|
+
|
|
69
|
+
// Auto-redacts in Debug/Display/Serialize/tracing
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Imports
|
|
73
|
+
|
|
74
|
+
Group in order:
|
|
75
|
+
1. `std` crates
|
|
76
|
+
2. External crates
|
|
77
|
+
3. Internal `loom-*` crates
|
|
78
|
+
|
|
79
|
+
```rust
|
|
80
|
+
use std::collections::HashMap;
|
|
81
|
+
|
|
82
|
+
use serde::{Deserialize, Serialize};
|
|
83
|
+
use tokio::sync::mpsc;
|
|
84
|
+
|
|
85
|
+
use loom_common_core::Message;
|
|
86
|
+
use loom_secret::SecretString;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Naming
|
|
90
|
+
|
|
91
|
+
| Category | Style |
|
|
92
|
+
|----------|-------|
|
|
93
|
+
| Functions/variables | `snake_case` |
|
|
94
|
+
| Types | `PascalCase` |
|
|
95
|
+
| Constants | `SCREAMING_CASE` |
|
|
96
|
+
|
|
97
|
+
## Comments
|
|
98
|
+
|
|
99
|
+
**Minimal comments.** Only add when code is complex and requires context.
|
|
100
|
+
|
|
101
|
+
**Copyright header required** on all files.
|
|
102
|
+
|
|
103
|
+
## Logging
|
|
104
|
+
|
|
105
|
+
Use `tracing` with structured fields:
|
|
106
|
+
|
|
107
|
+
```rust
|
|
108
|
+
use tracing::{info, instrument};
|
|
109
|
+
|
|
110
|
+
#[instrument(skip(self, secrets, large_args), fields(id = %id))]
|
|
111
|
+
async fn do_thing(&self, id: Uuid, secrets: SecretString) -> Result<()> {
|
|
112
|
+
info!("doing thing");
|
|
113
|
+
// ...
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Always skip secrets in instrument.**
|
|
118
|
+
|
|
119
|
+
## Testing
|
|
120
|
+
|
|
121
|
+
| Type | Use Case |
|
|
122
|
+
|------|----------|
|
|
123
|
+
| `proptest` | Property-based tests (preferred for complex logic) |
|
|
124
|
+
| Unit tests | Simple cases |
|
|
125
|
+
| Integration tests | End-to-end flows |
|
|
126
|
+
|
|
127
|
+
```rust
|
|
128
|
+
use proptest::prelude::*;
|
|
129
|
+
|
|
130
|
+
proptest! {
|
|
131
|
+
#[test]
|
|
132
|
+
fn test_invariant(input in any::<u32>()) {
|
|
133
|
+
prop_assert!(invariant_holds(input));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## i18n (Internationalization)
|
|
139
|
+
|
|
140
|
+
Uses GNU gettext with `.po` files:
|
|
141
|
+
|
|
142
|
+
```rust
|
|
143
|
+
use loom_i18n::{t, t_fmt, is_rtl, resolve_locale};
|
|
144
|
+
|
|
145
|
+
// Simple translation
|
|
146
|
+
let subject = t("es", "server.email.magic_link.subject");
|
|
147
|
+
|
|
148
|
+
// With variables
|
|
149
|
+
let body = t_fmt("es", "server.email.invitation.subject", &[
|
|
150
|
+
("org_name", "Acme Corp"),
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
// RTL support
|
|
154
|
+
if is_rtl(locale) {
|
|
155
|
+
// Use dir="rtl" in HTML
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**String naming**: `{prefix}.{domain}.{component}.{element}`
|
|
160
|
+
- Prefixes: `server.` (backend), `client.` (CLI)
|
|
161
|
+
- Domains: `email`, `api`, `auth`, `org`
|
|
162
|
+
|
|
163
|
+
**Supported locales**: `en`, `es`, `ar` (RTL)
|
|
164
|
+
|
|
165
|
+
## Svelte (Web)
|
|
166
|
+
|
|
167
|
+
See [web-frontend.md](web-frontend.md) for Svelte 5 patterns.
|
|
168
|
+
|
|
169
|
+
Key rule: **Always use Svelte 5 runes, never Svelte 4.**
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# LLM Proxy Architecture
|
|
2
|
+
|
|
3
|
+
Loom uses a server-side proxy for all LLM interactions. API keys stay on the server; clients communicate through proxy endpoints.
|
|
4
|
+
|
|
5
|
+
**Source**: `crates/loom-server-llm-proxy/`, `crates/loom-server-llm-service/`
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
┌─────────────┐ HTTP ┌─────────────┐ Provider API ┌─────────────┐
|
|
11
|
+
│ loom-cli │ ───────────────▶│ loom-server │ ──────────────────▶ │ Anthropic │
|
|
12
|
+
│ │ /proxy/{provider}│ │ │ OpenAI │
|
|
13
|
+
│ ProxyLlm- │ /complete │ LlmService │ │ Vertex AI │
|
|
14
|
+
│ Client │ /stream │ │ │ │
|
|
15
|
+
└─────────────┘ ◀───────────── └─────────────┘ ◀────────────────── └─────────────┘
|
|
16
|
+
SSE stream SSE stream
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Supported Providers
|
|
20
|
+
|
|
21
|
+
| Provider | Crate | Endpoint |
|
|
22
|
+
|----------|-------|----------|
|
|
23
|
+
| Anthropic (Claude) | `loom-server-llm-anthropic` | `/proxy/anthropic/stream` |
|
|
24
|
+
| OpenAI | `loom-server-llm-openai` | `/proxy/openai/stream` |
|
|
25
|
+
| Vertex AI | `loom-server-llm-vertex` | `/proxy/vertex/stream` |
|
|
26
|
+
|
|
27
|
+
## LlmClient Trait
|
|
28
|
+
|
|
29
|
+
```rust
|
|
30
|
+
pub trait LlmClient: Send + Sync {
|
|
31
|
+
async fn complete(&self, request: LlmRequest) -> Result<LlmResponse, LlmError>;
|
|
32
|
+
async fn complete_streaming(&self, request: LlmRequest) -> Result<LlmStream, LlmError>;
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Request/Response Types
|
|
37
|
+
|
|
38
|
+
```rust
|
|
39
|
+
pub struct LlmRequest {
|
|
40
|
+
pub model: String,
|
|
41
|
+
pub messages: Vec<Message>,
|
|
42
|
+
pub tools: Vec<ToolDefinition>,
|
|
43
|
+
pub max_tokens: Option<u32>,
|
|
44
|
+
pub temperature: Option<f32>,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
pub struct LlmResponse {
|
|
48
|
+
pub message: Message,
|
|
49
|
+
pub tool_calls: Vec<ToolCall>,
|
|
50
|
+
pub usage: Option<Usage>,
|
|
51
|
+
pub finish_reason: Option<String>,
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Streaming Events
|
|
56
|
+
|
|
57
|
+
SSE wire format uses `LlmStreamEvent`:
|
|
58
|
+
|
|
59
|
+
```rust
|
|
60
|
+
pub enum LlmStreamEvent {
|
|
61
|
+
TextDelta { content: String },
|
|
62
|
+
ToolCallDelta { call_id: String, tool_name: String, arguments_fragment: String },
|
|
63
|
+
ServerQuery(ServerQuery), // Phase 2: server→client queries
|
|
64
|
+
Completed { response: LlmProxyResponse },
|
|
65
|
+
Error { message: String },
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
SSE format:
|
|
70
|
+
```
|
|
71
|
+
event: llm
|
|
72
|
+
data: {"type":"text_delta","content":"Hello"}
|
|
73
|
+
|
|
74
|
+
event: llm
|
|
75
|
+
data: {"type":"completed","response":{...}}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## ProxyLlmClient (Client Side)
|
|
79
|
+
|
|
80
|
+
```rust
|
|
81
|
+
pub struct ProxyLlmClient {
|
|
82
|
+
base_url: String,
|
|
83
|
+
provider: LlmProvider,
|
|
84
|
+
http_client: reqwest::Client,
|
|
85
|
+
auth_token: Option<SecretString>,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Factory methods
|
|
89
|
+
ProxyLlmClient::anthropic("http://loom.server")
|
|
90
|
+
ProxyLlmClient::openai("http://loom.server")
|
|
91
|
+
ProxyLlmClient::vertex("http://loom.server")
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Stream Parsing
|
|
95
|
+
|
|
96
|
+
Client buffers SSE chunks until `\n\n` separator:
|
|
97
|
+
|
|
98
|
+
```rust
|
|
99
|
+
pub struct ProxyLlmStream {
|
|
100
|
+
inner: Pin<Box<dyn Stream<Item = Result<Bytes, reqwest::Error>> + Send>>,
|
|
101
|
+
buffer: String,
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Parsing flow:
|
|
106
|
+
1. Buffer bytes until `\n\n`
|
|
107
|
+
2. Extract `event: llm` and `data: {...}`
|
|
108
|
+
3. Parse JSON as `LlmStreamEvent`
|
|
109
|
+
4. Convert to `LlmEvent` for agent
|
|
110
|
+
|
|
111
|
+
## Server Query (Phase 2)
|
|
112
|
+
|
|
113
|
+
Enables server to request client actions during streaming:
|
|
114
|
+
|
|
115
|
+
```rust
|
|
116
|
+
pub enum ServerQueryKind {
|
|
117
|
+
ReadFile { path: String },
|
|
118
|
+
ExecuteCommand { command, args, timeout_secs },
|
|
119
|
+
RequestUserInput { prompt, input_type, options },
|
|
120
|
+
GetEnvironment { keys: Vec<String> },
|
|
121
|
+
GetWorkspaceContext,
|
|
122
|
+
Custom { name, payload },
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Currently scaffolded but not fully implemented.
|
|
127
|
+
|
|
128
|
+
## Security
|
|
129
|
+
|
|
130
|
+
- API keys stored server-side only
|
|
131
|
+
- Clients authenticate via session token
|
|
132
|
+
- No direct LLM API access from clients
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Agent State Machine
|
|
2
|
+
|
|
3
|
+
The core of Loom is an explicit, event-driven state machine that manages conversation flow and tool execution.
|
|
4
|
+
|
|
5
|
+
**Source**: `crates/loom-common-core/src/state.rs`, `crates/loom-common-core/src/agent.rs`
|
|
6
|
+
|
|
7
|
+
## Design Principles
|
|
8
|
+
|
|
9
|
+
1. **Predictable** - All transitions are explicit and testable
|
|
10
|
+
2. **Self-contained** - Each state carries its required context
|
|
11
|
+
3. **Pure** - No async in state machine; caller drives I/O
|
|
12
|
+
4. **Deterministic** - Same events → same actions (enables replay)
|
|
13
|
+
|
|
14
|
+
## States
|
|
15
|
+
|
|
16
|
+
| State | Description | Key Fields |
|
|
17
|
+
|-------|-------------|------------|
|
|
18
|
+
| `WaitingForUserInput` | Idle, ready for user message | `conversation` |
|
|
19
|
+
| `CallingLlm` | LLM request in flight | `conversation`, `retries` |
|
|
20
|
+
| `ProcessingLlmResponse` | Examining LLM response | `conversation`, `response` |
|
|
21
|
+
| `ExecutingTools` | Running tool calls in parallel | `conversation`, `executions` |
|
|
22
|
+
| `PostToolsHook` | Running post-tool hooks (auto-commit) | `conversation`, `pending_llm_request`, `completed_tools` |
|
|
23
|
+
| `Error` | Recoverable error with retry | `conversation`, `error`, `retries`, `origin` |
|
|
24
|
+
| `ShuttingDown` | Terminal state | (none) |
|
|
25
|
+
|
|
26
|
+
## Events
|
|
27
|
+
|
|
28
|
+
| Event | Description |
|
|
29
|
+
|-------|-------------|
|
|
30
|
+
| `UserInput(Message)` | User submitted message |
|
|
31
|
+
| `LlmEvent::TextDelta` | Streaming text from LLM |
|
|
32
|
+
| `LlmEvent::ToolCallDelta` | Streaming tool call data |
|
|
33
|
+
| `LlmEvent::Completed(LlmResponse)` | LLM finished |
|
|
34
|
+
| `LlmEvent::Error(LlmError)` | LLM error |
|
|
35
|
+
| `ToolCompleted { call_id, outcome }` | Tool execution finished |
|
|
36
|
+
| `PostToolsHookCompleted` | Hooks finished |
|
|
37
|
+
| `RetryTimeoutFired` | Retry backoff expired |
|
|
38
|
+
| `ShutdownRequested` | Graceful shutdown |
|
|
39
|
+
|
|
40
|
+
## Actions
|
|
41
|
+
|
|
42
|
+
Actions are returned to caller indicating what I/O to perform:
|
|
43
|
+
|
|
44
|
+
| Action | Description |
|
|
45
|
+
|--------|-------------|
|
|
46
|
+
| `SendLlmRequest(LlmRequest)` | Call LLM provider |
|
|
47
|
+
| `ExecuteTools(Vec<ToolCall>)` | Run tool calls |
|
|
48
|
+
| `RunPostToolsHook { completed_tools }` | Run auto-commit |
|
|
49
|
+
| `WaitForInput` | Idle |
|
|
50
|
+
| `DisplayMessage(String)` | Show to user |
|
|
51
|
+
| `DisplayError(String)` | Show error |
|
|
52
|
+
| `Shutdown` | Terminate |
|
|
53
|
+
|
|
54
|
+
## Transition Table
|
|
55
|
+
|
|
56
|
+
| Current State | Event | New State | Action |
|
|
57
|
+
|---------------|-------|-----------|--------|
|
|
58
|
+
| WaitingForUserInput | UserInput | CallingLlm | SendLlmRequest |
|
|
59
|
+
| CallingLlm | TextDelta | CallingLlm | DisplayMessage |
|
|
60
|
+
| CallingLlm | Completed | ProcessingLlmResponse | (internal) |
|
|
61
|
+
| CallingLlm | Error (retries < max) | Error | WaitForInput |
|
|
62
|
+
| CallingLlm | Error (retries >= max) | WaitingForUserInput | DisplayError |
|
|
63
|
+
| ProcessingLlmResponse | (has tool calls) | ExecutingTools | ExecuteTools |
|
|
64
|
+
| ProcessingLlmResponse | (no tools) | WaitingForUserInput | WaitForInput |
|
|
65
|
+
| ExecutingTools | ToolCompleted (some pending) | ExecutingTools | WaitForInput |
|
|
66
|
+
| ExecutingTools | ToolCompleted (all done, mutating) | PostToolsHook | RunPostToolsHook |
|
|
67
|
+
| ExecutingTools | ToolCompleted (all done, non-mutating) | CallingLlm | SendLlmRequest |
|
|
68
|
+
| PostToolsHook | PostToolsHookCompleted | CallingLlm | SendLlmRequest |
|
|
69
|
+
| Error | RetryTimeoutFired | CallingLlm | SendLlmRequest |
|
|
70
|
+
| *any* | ShutdownRequested | ShuttingDown | Shutdown |
|
|
71
|
+
|
|
72
|
+
## Flow Diagram
|
|
73
|
+
|
|
74
|
+
```mermaid
|
|
75
|
+
stateDiagram-v2
|
|
76
|
+
[*] --> WaitingForUserInput : Agent::new()
|
|
77
|
+
|
|
78
|
+
WaitingForUserInput --> CallingLlm : UserInput
|
|
79
|
+
|
|
80
|
+
CallingLlm --> CallingLlm : TextDelta / ToolCallDelta
|
|
81
|
+
CallingLlm --> ProcessingLlmResponse : Completed
|
|
82
|
+
CallingLlm --> Error : Error (retries < max)
|
|
83
|
+
CallingLlm --> WaitingForUserInput : Error (retries >= max)
|
|
84
|
+
|
|
85
|
+
ProcessingLlmResponse --> ExecutingTools : has tool calls
|
|
86
|
+
ProcessingLlmResponse --> WaitingForUserInput : no tool calls
|
|
87
|
+
|
|
88
|
+
ExecutingTools --> ExecutingTools : ToolCompleted (some pending)
|
|
89
|
+
ExecutingTools --> PostToolsHook : ToolCompleted (all done, mutating)
|
|
90
|
+
ExecutingTools --> CallingLlm : ToolCompleted (all done, no mutation)
|
|
91
|
+
|
|
92
|
+
PostToolsHook --> CallingLlm : PostToolsHookCompleted
|
|
93
|
+
|
|
94
|
+
Error --> CallingLlm : RetryTimeoutFired
|
|
95
|
+
|
|
96
|
+
WaitingForUserInput --> ShuttingDown : ShutdownRequested
|
|
97
|
+
CallingLlm --> ShuttingDown : ShutdownRequested
|
|
98
|
+
ExecutingTools --> ShuttingDown : ShutdownRequested
|
|
99
|
+
|
|
100
|
+
ShuttingDown --> [*]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Mutating Tools
|
|
104
|
+
|
|
105
|
+
Tools that modify files trigger `PostToolsHook` for auto-commit:
|
|
106
|
+
- `edit_file`
|
|
107
|
+
- `bash`
|
|
108
|
+
|
|
109
|
+
Non-mutating tools skip hooks and go directly back to `CallingLlm`.
|
|
110
|
+
|
|
111
|
+
## Key Implementation Pattern
|
|
112
|
+
|
|
113
|
+
```rust
|
|
114
|
+
// Agent is purely reactive - no async code
|
|
115
|
+
pub fn handle_event(&mut self, event: AgentEvent) -> AgentResult<AgentAction> {
|
|
116
|
+
match (&mut self.state, event) {
|
|
117
|
+
(AgentState::WaitingForUserInput { conversation }, AgentEvent::UserInput(msg)) => {
|
|
118
|
+
conversation.messages.push(msg);
|
|
119
|
+
self.state = AgentState::CallingLlm { conversation: conversation.clone(), retries: 0 };
|
|
120
|
+
Ok(AgentAction::SendLlmRequest(request))
|
|
121
|
+
}
|
|
122
|
+
// ... more transitions
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Inversion of control**: Caller executes actions (LLM calls, tool runs) and feeds events back. Enables:
|
|
128
|
+
- Offline operation
|
|
129
|
+
- Different async runtimes
|
|
130
|
+
- Comprehensive testing
|
|
131
|
+
- Replay/debugging
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Thread System
|
|
2
|
+
|
|
3
|
+
Threads persist complete conversation snapshots including messages, agent state, and git context.
|
|
4
|
+
|
|
5
|
+
**Source**: `crates/loom-common-thread/src/`
|
|
6
|
+
|
|
7
|
+
## Thread Structure
|
|
8
|
+
|
|
9
|
+
```rust
|
|
10
|
+
pub struct Thread {
|
|
11
|
+
pub id: ThreadId, // T-{uuid7}
|
|
12
|
+
pub version: u64, // Optimistic locking
|
|
13
|
+
|
|
14
|
+
// Timestamps
|
|
15
|
+
pub created_at: String,
|
|
16
|
+
pub updated_at: String,
|
|
17
|
+
pub last_activity_at: String,
|
|
18
|
+
|
|
19
|
+
// Environment snapshot
|
|
20
|
+
pub workspace_root: Option<String>,
|
|
21
|
+
pub cwd: Option<String>,
|
|
22
|
+
pub loom_version: Option<String>,
|
|
23
|
+
|
|
24
|
+
// Git context
|
|
25
|
+
pub git_branch: Option<String>,
|
|
26
|
+
pub git_remote_url: Option<String>,
|
|
27
|
+
pub git_initial_branch: Option<String>,
|
|
28
|
+
pub git_initial_commit_sha: Option<String>,
|
|
29
|
+
pub git_current_commit_sha: Option<String>,
|
|
30
|
+
pub git_start_dirty: Option<bool>,
|
|
31
|
+
pub git_end_dirty: Option<bool>,
|
|
32
|
+
pub git_commits: Vec<String>, // All commits observed
|
|
33
|
+
|
|
34
|
+
// Conversation
|
|
35
|
+
pub provider: Option<String>,
|
|
36
|
+
pub model: Option<String>,
|
|
37
|
+
pub conversation: ConversationSnapshot,
|
|
38
|
+
pub agent_state: AgentStateSnapshot,
|
|
39
|
+
|
|
40
|
+
// Metadata
|
|
41
|
+
pub metadata: ThreadMetadata, // Title, tags, pinned
|
|
42
|
+
pub visibility: ThreadVisibility, // Organization/Private/Public
|
|
43
|
+
pub is_private: bool, // Local-only (no sync)
|
|
44
|
+
pub is_shared_with_support: bool,
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## ThreadStore Trait
|
|
49
|
+
|
|
50
|
+
```rust
|
|
51
|
+
pub trait ThreadStore: Send + Sync {
|
|
52
|
+
async fn load(&self, id: &ThreadId) -> Result<Option<Thread>, ThreadStoreError>;
|
|
53
|
+
async fn save(&self, thread: &Thread) -> Result<(), ThreadStoreError>;
|
|
54
|
+
async fn list(&self, limit: u32) -> Result<Vec<ThreadSummary>, ThreadStoreError>;
|
|
55
|
+
async fn delete(&self, id: &ThreadId) -> Result<(), ThreadStoreError>;
|
|
56
|
+
async fn save_and_sync(&self, thread: &Thread) -> Result<(), ThreadStoreError>;
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## LocalThreadStore
|
|
61
|
+
|
|
62
|
+
Default implementation for local persistence:
|
|
63
|
+
|
|
64
|
+
- **Location**: `~/.local/share/loom/threads/{ThreadId}.json`
|
|
65
|
+
- **Atomic writes**: temp file + rename
|
|
66
|
+
- **Search**: FTS5 across title, branch, messages, tags, commit SHAs
|
|
67
|
+
|
|
68
|
+
## ConversationSnapshot
|
|
69
|
+
|
|
70
|
+
```rust
|
|
71
|
+
pub struct ConversationSnapshot {
|
|
72
|
+
pub messages: Vec<Message>,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
pub struct Message {
|
|
76
|
+
pub role: Role, // System, User, Assistant, Tool
|
|
77
|
+
pub content: String,
|
|
78
|
+
pub tool_call_id: Option<String>,
|
|
79
|
+
pub name: Option<String>,
|
|
80
|
+
pub tool_calls: Vec<ToolCall>,
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## ThreadMetadata
|
|
85
|
+
|
|
86
|
+
```rust
|
|
87
|
+
pub struct ThreadMetadata {
|
|
88
|
+
pub title: Option<String>,
|
|
89
|
+
pub tags: Vec<String>,
|
|
90
|
+
pub pinned: bool,
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Visibility
|
|
95
|
+
|
|
96
|
+
```rust
|
|
97
|
+
pub enum ThreadVisibility {
|
|
98
|
+
Organization, // Visible to org members
|
|
99
|
+
Private, // Only visible to owner
|
|
100
|
+
Public, // Publicly shareable
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Key Features
|
|
105
|
+
|
|
106
|
+
1. **Complete snapshot** - Full conversation + agent state + git context
|
|
107
|
+
2. **Optimistic locking** - Version numbers prevent conflicts
|
|
108
|
+
3. **FTS5 search** - Full-text across all thread content
|
|
109
|
+
4. **Local-first** - `is_private` keeps threads from syncing
|
|
110
|
+
5. **Git tracking** - Captures branch, commits, dirty state
|
|
111
|
+
|
|
112
|
+
## Persistence Flow
|
|
113
|
+
|
|
114
|
+
1. Agent processes events
|
|
115
|
+
2. Thread updated after each action
|
|
116
|
+
3. `save()` writes atomic JSON
|
|
117
|
+
4. Optional `save_and_sync()` for server sync
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Tool System
|
|
2
|
+
|
|
3
|
+
Tools extend the agent's capabilities to perform file operations, execute commands, and search the web.
|
|
4
|
+
|
|
5
|
+
**Source**: `crates/loom-cli-tools/src/`
|
|
6
|
+
|
|
7
|
+
## Tool Trait
|
|
8
|
+
|
|
9
|
+
```rust
|
|
10
|
+
#[async_trait]
|
|
11
|
+
pub trait Tool: Send + Sync {
|
|
12
|
+
fn name(&self) -> &str;
|
|
13
|
+
fn description(&self) -> &str;
|
|
14
|
+
fn input_schema(&self) -> serde_json::Value;
|
|
15
|
+
async fn invoke(&self, args: serde_json::Value, ctx: &ToolContext) -> Result<serde_json::Value, ToolError>;
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## ToolRegistry
|
|
20
|
+
|
|
21
|
+
HashMap-based dynamic dispatch for tool lookup:
|
|
22
|
+
|
|
23
|
+
```rust
|
|
24
|
+
pub struct ToolRegistry {
|
|
25
|
+
tools: HashMap<String, Arc<dyn Tool>>,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
impl ToolRegistry {
|
|
29
|
+
pub fn register(&mut self, tool: Arc<dyn Tool>);
|
|
30
|
+
pub fn get(&self, name: &str) -> Option<Arc<dyn Tool>>;
|
|
31
|
+
pub fn list(&self) -> Vec<&str>;
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Built-in Tools
|
|
36
|
+
|
|
37
|
+
### File Operations
|
|
38
|
+
|
|
39
|
+
| Tool | Purpose |
|
|
40
|
+
|------|---------|
|
|
41
|
+
| `ReadFileTool` | Read file contents (truncates large files) |
|
|
42
|
+
| `ListFilesTool` | Directory listing with path validation |
|
|
43
|
+
| `EditFileTool` | Snippet-based text replacement (atomic edits) |
|
|
44
|
+
|
|
45
|
+
### Command Execution
|
|
46
|
+
|
|
47
|
+
| Tool | Purpose |
|
|
48
|
+
|------|---------|
|
|
49
|
+
| `BashTool` | Execute shell commands with timeout/truncation |
|
|
50
|
+
|
|
51
|
+
**Note**: `BashTool` is a "mutating" tool that triggers `PostToolsHook` for auto-commit.
|
|
52
|
+
|
|
53
|
+
### LLM/Search
|
|
54
|
+
|
|
55
|
+
| Tool | Purpose |
|
|
56
|
+
|------|---------|
|
|
57
|
+
| `OracleTool` | Consult secondary LLM via server proxy |
|
|
58
|
+
| `WebSearchToolGoogle` | Web search via Google CSE |
|
|
59
|
+
| `WebSearchToolSerper` | Web search via Serper API |
|
|
60
|
+
|
|
61
|
+
## Security
|
|
62
|
+
|
|
63
|
+
All tools validate paths against workspace boundaries:
|
|
64
|
+
- Prevents directory traversal (`../`)
|
|
65
|
+
- Restricts to project root
|
|
66
|
+
- Validates file existence
|
|
67
|
+
|
|
68
|
+
## Tool Execution Flow
|
|
69
|
+
|
|
70
|
+
1. LLM returns `tool_calls` in response
|
|
71
|
+
2. Agent transitions to `ExecutingTools` state
|
|
72
|
+
3. Caller invokes tools via `ToolRegistry.get(name).invoke(args, ctx)`
|
|
73
|
+
4. Each tool returns success/error outcome
|
|
74
|
+
5. Results collected in `ToolExecutionStatus`
|
|
75
|
+
6. When all complete → check if mutating → `PostToolsHook` or `CallingLlm`
|
|
76
|
+
|
|
77
|
+
## ToolContext
|
|
78
|
+
|
|
79
|
+
Passed to every tool invocation:
|
|
80
|
+
|
|
81
|
+
```rust
|
|
82
|
+
pub struct ToolContext {
|
|
83
|
+
pub workspace_root: PathBuf,
|
|
84
|
+
pub cwd: PathBuf,
|
|
85
|
+
pub thread_id: Option<ThreadId>,
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Adding a New Tool
|
|
90
|
+
|
|
91
|
+
1. Implement the `Tool` trait
|
|
92
|
+
2. Register with `ToolRegistry`
|
|
93
|
+
3. Add to tool definitions sent to LLM
|
|
94
|
+
4. If mutating, ensure it triggers `PostToolsHook`
|