ada-agent 0.1.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/LICENSE +21 -0
- package/README.md +256 -0
- package/bench/README.md +88 -0
- package/bench/swebench.mjs +242 -0
- package/bin/ada-server.mjs +6 -0
- package/bin/ada.mjs +7 -0
- package/docs/agent-loop.svg +66 -0
- package/docs/architecture.md +139 -0
- package/docs/architecture.svg +73 -0
- package/docs/connectors.md +48 -0
- package/docs/integrations.md +59 -0
- package/docs/login-flow.svg +56 -0
- package/docs/orchestration.md +45 -0
- package/package.json +64 -0
- package/skills/accessibility/SKILL.md +23 -0
- package/skills/add-logging/SKILL.md +23 -0
- package/skills/add-metrics/SKILL.md +23 -0
- package/skills/adr/SKILL.md +24 -0
- package/skills/aesthetic-direction/SKILL.md +24 -0
- package/skills/agent-loop/SKILL.md +23 -0
- package/skills/alerting/SKILL.md +23 -0
- package/skills/alpha-compositing/SKILL.md +23 -0
- package/skills/android-compose/SKILL.md +23 -0
- package/skills/angular-module/SKILL.md +23 -0
- package/skills/ansible-playbook/SKILL.md +24 -0
- package/skills/api-docs/SKILL.md +24 -0
- package/skills/app-store-prep/SKILL.md +23 -0
- package/skills/architecture-diagram/SKILL.md +21 -0
- package/skills/architecture-doc/SKILL.md +24 -0
- package/skills/audit-log/SKILL.md +23 -0
- package/skills/authz-review/SKILL.md +23 -0
- package/skills/aws-lambda/SKILL.md +24 -0
- package/skills/bash-script/SKILL.md +23 -0
- package/skills/batch/SKILL.md +23 -0
- package/skills/bisect/SKILL.md +23 -0
- package/skills/bounding-box/SKILL.md +24 -0
- package/skills/branch-cleanup/SKILL.md +23 -0
- package/skills/bundle-analyze/SKILL.md +23 -0
- package/skills/cache/SKILL.md +23 -0
- package/skills/call-graph/SKILL.md +23 -0
- package/skills/canvas-debug/SKILL.md +23 -0
- package/skills/cdn-setup/SKILL.md +23 -0
- package/skills/changelog/SKILL.md +24 -0
- package/skills/cherry-pick/SKILL.md +23 -0
- package/skills/ci-setup/SKILL.md +23 -0
- package/skills/cleanup/SKILL.md +23 -0
- package/skills/cli-tool/SKILL.md +23 -0
- package/skills/cloudformation/SKILL.md +23 -0
- package/skills/code-examples/SKILL.md +24 -0
- package/skills/code-review/SKILL.md +23 -0
- package/skills/color-palette/SKILL.md +24 -0
- package/skills/color-space/SKILL.md +24 -0
- package/skills/comment-why/SKILL.md +23 -0
- package/skills/commit/SKILL.md +26 -0
- package/skills/complexity-audit/SKILL.md +23 -0
- package/skills/component/SKILL.md +23 -0
- package/skills/component-library/SKILL.md +23 -0
- package/skills/connect-github/SKILL.md +20 -0
- package/skills/connect-mcp/SKILL.md +21 -0
- package/skills/connect-postgres/SKILL.md +20 -0
- package/skills/connect-remote/SKILL.md +23 -0
- package/skills/connect-slack/SKILL.md +20 -0
- package/skills/contract-audit/SKILL.md +25 -0
- package/skills/contributing/SKILL.md +23 -0
- package/skills/cpp-raii/SKILL.md +23 -0
- package/skills/cron-job/SKILL.md +23 -0
- package/skills/cv-preprocess/SKILL.md +24 -0
- package/skills/dark-mode/SKILL.md +24 -0
- package/skills/dashboard/SKILL.md +23 -0
- package/skills/dashboard-ui/SKILL.md +23 -0
- package/skills/data-export/SKILL.md +23 -0
- package/skills/data-validation/SKILL.md +23 -0
- package/skills/dataframe/SKILL.md +23 -0
- package/skills/db-index/SKILL.md +24 -0
- package/skills/dead-code/SKILL.md +23 -0
- package/skills/debug/SKILL.md +24 -0
- package/skills/deck-review/SKILL.md +24 -0
- package/skills/dedupe/SKILL.md +23 -0
- package/skills/dedupe-deps/SKILL.md +23 -0
- package/skills/dependency-audit/SKILL.md +23 -0
- package/skills/dependency-update/SKILL.md +23 -0
- package/skills/deploy/SKILL.md +23 -0
- package/skills/design-system/SKILL.md +24 -0
- package/skills/design-tokens/SKILL.md +24 -0
- package/skills/diagram-as-code/SKILL.md +24 -0
- package/skills/diff-explain/SKILL.md +23 -0
- package/skills/django-view/SKILL.md +23 -0
- package/skills/doc-lint/SKILL.md +24 -0
- package/skills/docker-compose/SKILL.md +23 -0
- package/skills/dockerize/SKILL.md +23 -0
- package/skills/docstrings/SKILL.md +23 -0
- package/skills/dotfiles/SKILL.md +23 -0
- package/skills/dpi-scaling/SKILL.md +23 -0
- package/skills/e2e-test/SKILL.md +23 -0
- package/skills/embeddings/SKILL.md +23 -0
- package/skills/empty-states/SKILL.md +23 -0
- package/skills/env-setup/SKILL.md +23 -0
- package/skills/erc20/SKILL.md +24 -0
- package/skills/error-tracking/SKILL.md +23 -0
- package/skills/estimate/SKILL.md +23 -0
- package/skills/etl-pipeline/SKILL.md +24 -0
- package/skills/eval-harness/SKILL.md +23 -0
- package/skills/exif-orientation/SKILL.md +23 -0
- package/skills/explain-code/SKILL.md +23 -0
- package/skills/express-middleware/SKILL.md +23 -0
- package/skills/extract-function/SKILL.md +23 -0
- package/skills/faq/SKILL.md +24 -0
- package/skills/fastapi-endpoint/SKILL.md +23 -0
- package/skills/favicon/SKILL.md +23 -0
- package/skills/feature-engineering/SKILL.md +23 -0
- package/skills/few-shot/SKILL.md +23 -0
- package/skills/find-owner/SKILL.md +23 -0
- package/skills/firmware-driver/SKILL.md +23 -0
- package/skills/fix-flaky-tests/SKILL.md +23 -0
- package/skills/flutter-widget/SKILL.md +23 -0
- package/skills/font-rendering/SKILL.md +23 -0
- package/skills/form-validation/SKILL.md +23 -0
- package/skills/format/SKILL.md +23 -0
- package/skills/game-loop/SKILL.md +23 -0
- package/skills/gas-optimize/SKILL.md +25 -0
- package/skills/gdpr-review/SKILL.md +24 -0
- package/skills/github-actions/SKILL.md +23 -0
- package/skills/glossary/SKILL.md +24 -0
- package/skills/go-idioms/SKILL.md +23 -0
- package/skills/gpu-profile/SKILL.md +23 -0
- package/skills/graphify/SKILL.md +21 -0
- package/skills/graphql-resolver/SKILL.md +23 -0
- package/skills/grpc-service/SKILL.md +23 -0
- package/skills/guardrails/SKILL.md +23 -0
- package/skills/healthcheck/SKILL.md +23 -0
- package/skills/heisenbug/SKILL.md +23 -0
- package/skills/helm-chart/SKILL.md +24 -0
- package/skills/hero-section/SKILL.md +23 -0
- package/skills/html-email/SKILL.md +24 -0
- package/skills/html-form/SKILL.md +23 -0
- package/skills/html-sanitize/SKILL.md +23 -0
- package/skills/html-table/SKILL.md +23 -0
- package/skills/html-to-pdf/SKILL.md +23 -0
- package/skills/http-client/SKILL.md +23 -0
- package/skills/i18n/SKILL.md +23 -0
- package/skills/i2c-spi/SKILL.md +23 -0
- package/skills/image-decode/SKILL.md +24 -0
- package/skills/image-memory/SKILL.md +24 -0
- package/skills/image-perf/SKILL.md +24 -0
- package/skills/image-pipeline/SKILL.md +24 -0
- package/skills/image-upload/SKILL.md +24 -0
- package/skills/infra-cost/SKILL.md +24 -0
- package/skills/input-validation/SKILL.md +23 -0
- package/skills/issue-template/SKILL.md +23 -0
- package/skills/java-streams/SKILL.md +23 -0
- package/skills/k8s-manifest/SKILL.md +23 -0
- package/skills/kotlin-coroutines/SKILL.md +23 -0
- package/skills/landing-page/SKILL.md +24 -0
- package/skills/laravel-controller/SKILL.md +23 -0
- package/skills/lazy-load/SKILL.md +23 -0
- package/skills/license-check/SKILL.md +23 -0
- package/skills/license-header/SKILL.md +23 -0
- package/skills/lint-fix/SKILL.md +23 -0
- package/skills/llm-cost/SKILL.md +23 -0
- package/skills/lockfile-fix/SKILL.md +23 -0
- package/skills/low-power/SKILL.md +23 -0
- package/skills/makefile/SKILL.md +23 -0
- package/skills/man-page/SKILL.md +24 -0
- package/skills/mcp-server/SKILL.md +23 -0
- package/skills/memory-leak/SKILL.md +23 -0
- package/skills/mermaid-diagram/SKILL.md +23 -0
- package/skills/meta-tags/SKILL.md +23 -0
- package/skills/micro-interactions/SKILL.md +23 -0
- package/skills/migration/SKILL.md +23 -0
- package/skills/migration-guide/SKILL.md +24 -0
- package/skills/mkdocs-setup/SKILL.md +24 -0
- package/skills/mobile-permissions/SKILL.md +23 -0
- package/skills/mock-api/SKILL.md +23 -0
- package/skills/modernize/SKILL.md +23 -0
- package/skills/monorepo-setup/SKILL.md +23 -0
- package/skills/motion-design/SKILL.md +23 -0
- package/skills/n-plus-one/SKILL.md +23 -0
- package/skills/naming-review/SKILL.md +23 -0
- package/skills/nextjs-route/SKILL.md +23 -0
- package/skills/nginx-config/SKILL.md +23 -0
- package/skills/ocr-debug/SKILL.md +24 -0
- package/skills/onboard/SKILL.md +23 -0
- package/skills/onboarding-map/SKILL.md +23 -0
- package/skills/open-pr/SKILL.md +24 -0
- package/skills/openapi/SKILL.md +23 -0
- package/skills/opencv-debug/SKILL.md +24 -0
- package/skills/orm-model/SKILL.md +23 -0
- package/skills/owasp-check/SKILL.md +24 -0
- package/skills/page-transitions/SKILL.md +23 -0
- package/skills/pagination/SKILL.md +23 -0
- package/skills/perf-optimize/SKILL.md +23 -0
- package/skills/perf-profile/SKILL.md +23 -0
- package/skills/physics/SKILL.md +23 -0
- package/skills/pitch-deck/SKILL.md +24 -0
- package/skills/pixel-diff/SKILL.md +23 -0
- package/skills/ponytail/SKILL.md +46 -0
- package/skills/postmortem/SKILL.md +24 -0
- package/skills/pptx-deck/SKILL.md +23 -0
- package/skills/pptx-export/SKILL.md +23 -0
- package/skills/pptx-from-markdown/SKILL.md +23 -0
- package/skills/pptx-template/SKILL.md +24 -0
- package/skills/pr-review/SKILL.md +24 -0
- package/skills/precommit/SKILL.md +23 -0
- package/skills/pricing-page/SKILL.md +23 -0
- package/skills/project-overview/SKILL.md +22 -0
- package/skills/prompt-template/SKILL.md +23 -0
- package/skills/property-test/SKILL.md +23 -0
- package/skills/protobuf/SKILL.md +23 -0
- package/skills/py-async/SKILL.md +23 -0
- package/skills/py-typing/SKILL.md +23 -0
- package/skills/query-optimize/SKILL.md +23 -0
- package/skills/rag-pipeline/SKILL.md +23 -0
- package/skills/rails-resource/SKILL.md +23 -0
- package/skills/rate-limit/SKILL.md +23 -0
- package/skills/react-hooks/SKILL.md +23 -0
- package/skills/react-native-screen/SKILL.md +23 -0
- package/skills/react-perf/SKILL.md +23 -0
- package/skills/readme/SKILL.md +24 -0
- package/skills/rebase/SKILL.md +24 -0
- package/skills/refactor/SKILL.md +23 -0
- package/skills/regression-test/SKILL.md +23 -0
- package/skills/release-notes/SKILL.md +24 -0
- package/skills/rename-symbol/SKILL.md +23 -0
- package/skills/repro/SKILL.md +23 -0
- package/skills/resolve-conflicts/SKILL.md +23 -0
- package/skills/responsive/SKILL.md +23 -0
- package/skills/rest-endpoint/SKILL.md +23 -0
- package/skills/retro/SKILL.md +23 -0
- package/skills/rtos-task/SKILL.md +23 -0
- package/skills/runbook/SKILL.md +25 -0
- package/skills/rust-borrow/SKILL.md +23 -0
- package/skills/rust-unsafe-audit/SKILL.md +23 -0
- package/skills/sanitize/SKILL.md +23 -0
- package/skills/schema-design/SKILL.md +23 -0
- package/skills/screenshot-debug/SKILL.md +22 -0
- package/skills/scroll-animation/SKILL.md +23 -0
- package/skills/secret-scan/SKILL.md +23 -0
- package/skills/security-audit/SKILL.md +23 -0
- package/skills/security-review/SKILL.md +23 -0
- package/skills/seed-data/SKILL.md +23 -0
- package/skills/self-review/SKILL.md +23 -0
- package/skills/semantic-html/SKILL.md +23 -0
- package/skills/semver-bump/SKILL.md +24 -0
- package/skills/shader/SKILL.md +23 -0
- package/skills/shader-debug/SKILL.md +23 -0
- package/skills/simplify-conditionals/SKILL.md +23 -0
- package/skills/sitemap/SKILL.md +23 -0
- package/skills/skeleton-loader/SKILL.md +23 -0
- package/skills/slide-charts/SKILL.md +24 -0
- package/skills/slide-outline/SKILL.md +23 -0
- package/skills/snapshot-update/SKILL.md +23 -0
- package/skills/solidity-contract/SKILL.md +25 -0
- package/skills/speaker-notes/SKILL.md +23 -0
- package/skills/spike/SKILL.md +23 -0
- package/skills/split-file/SKILL.md +23 -0
- package/skills/spring-controller/SKILL.md +23 -0
- package/skills/sprite-anim/SKILL.md +23 -0
- package/skills/sql-report/SKILL.md +23 -0
- package/skills/squash/SKILL.md +24 -0
- package/skills/ssl-setup/SKILL.md +23 -0
- package/skills/stacktrace/SKILL.md +23 -0
- package/skills/static-site/SKILL.md +24 -0
- package/skills/structured-logging/SKILL.md +23 -0
- package/skills/svelte-store/SKILL.md +23 -0
- package/skills/swiftui-view/SKILL.md +23 -0
- package/skills/tailwind-theme/SKILL.md +24 -0
- package/skills/tcp-server/SKILL.md +23 -0
- package/skills/tdd/SKILL.md +23 -0
- package/skills/terraform-module/SKILL.md +24 -0
- package/skills/test-coverage/SKILL.md +23 -0
- package/skills/texture-debug/SKILL.md +23 -0
- package/skills/threat-model/SKILL.md +23 -0
- package/skills/thumbnail/SKILL.md +24 -0
- package/skills/todo-scan/SKILL.md +23 -0
- package/skills/tool-definition/SKILL.md +23 -0
- package/skills/trace-flow/SKILL.md +23 -0
- package/skills/tracing/SKILL.md +23 -0
- package/skills/train-model/SKILL.md +24 -0
- package/skills/tree-shake/SKILL.md +23 -0
- package/skills/ts-generics/SKILL.md +23 -0
- package/skills/ts-strict/SKILL.md +23 -0
- package/skills/tui-app/SKILL.md +23 -0
- package/skills/tutorial/SKILL.md +24 -0
- package/skills/type-tighten/SKILL.md +23 -0
- package/skills/typography/SKILL.md +24 -0
- package/skills/ui-bug-repro/SKILL.md +23 -0
- package/skills/ui-polish/SKILL.md +24 -0
- package/skills/ui-review/SKILL.md +24 -0
- package/skills/vendor/SKILL.md +23 -0
- package/skills/visual-diff-ci/SKILL.md +24 -0
- package/skills/visual-regression/SKILL.md +23 -0
- package/skills/vue-composition/SKILL.md +23 -0
- package/skills/web-component/SKILL.md +23 -0
- package/skills/web-fonts/SKILL.md +24 -0
- package/skills/web3-frontend/SKILL.md +25 -0
- package/skills/webgl-debug/SKILL.md +23 -0
- package/skills/webhook/SKILL.md +23 -0
- package/skills/websocket/SKILL.md +23 -0
- package/skills/write-tests/SKILL.md +19 -0
- package/src/client/agent.ts +803 -0
- package/src/client/background.ts +39 -0
- package/src/client/checkpoint.ts +48 -0
- package/src/client/cli.ts +1253 -0
- package/src/client/compaction.ts +86 -0
- package/src/client/extensions.ts +83 -0
- package/src/client/hooks.ts +40 -0
- package/src/client/image.ts +26 -0
- package/src/client/lsp.ts +0 -0
- package/src/client/mcp.ts +276 -0
- package/src/client/models-dev.ts +52 -0
- package/src/client/pkg.ts +41 -0
- package/src/client/platform.ts +94 -0
- package/src/client/prompts.ts +47 -0
- package/src/client/render.ts +138 -0
- package/src/client/session.ts +107 -0
- package/src/client/settings.ts +86 -0
- package/src/client/skill-router.ts +79 -0
- package/src/client/skills.ts +199 -0
- package/src/client/snapshot.ts +56 -0
- package/src/client/telemetry.ts +24 -0
- package/src/client/todos.ts +23 -0
- package/src/client/tools.ts +756 -0
- package/src/client/tui-mode.ts +41 -0
- package/src/client/tui.ts +224 -0
- package/src/sdk/index.ts +36 -0
- package/src/selfcheck.ts +364 -0
- package/src/server/config.ts +58 -0
- package/src/server/credentials.ts +89 -0
- package/src/server/identity.ts +58 -0
- package/src/server/index.ts +113 -0
- package/src/server/oauth.ts +93 -0
- package/src/server/providers/adapter.ts +25 -0
- package/src/server/providers/anthropic.ts +189 -0
- package/src/server/providers/openai-compat.ts +76 -0
- package/src/server/providers/registry.ts +31 -0
- package/src/server/router.ts +29 -0
- package/src/server/sse.ts +20 -0
- package/src/shared/types.ts +20 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// Skills: SKILL.md files in .ada/skills/<name>/SKILL.md (or .ada/skills/<name>.md), project or
|
|
2
|
+
// global, plus the ones bundled with ada. A `list_skills` tool lets the model browse them on demand
|
|
3
|
+
// (so the per-request tool surface stays small even with hundreds of skills); `use_skill` then
|
|
4
|
+
// returns the full instructions for one by name.
|
|
5
|
+
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { homedir } from "node:os";
|
|
8
|
+
import { dirname, join, resolve } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { registerTool } from "./tools.ts";
|
|
11
|
+
import { confidentSkill, rankSkills } from "./skill-router.ts";
|
|
12
|
+
|
|
13
|
+
let LOADED: Skill[] = []; // the skills registered this session — for routeSkills()
|
|
14
|
+
|
|
15
|
+
/** Rank the loaded skills by relevance to a request (used by find_skill + the agent's auto-suggest). */
|
|
16
|
+
export function routeSkills(query: string, n = 5): { name: string; description: string; score: number }[] {
|
|
17
|
+
return rankSkills(query, LOADED, n);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** A skill's instructions (the SKILL.md body, front-matter stripped), or null if not loaded. */
|
|
21
|
+
export function skillBody(name: string): string | null {
|
|
22
|
+
const s = LOADED.find((x) => x.name === name);
|
|
23
|
+
if (!s) return null;
|
|
24
|
+
try {
|
|
25
|
+
return readFileSync(s.path, "utf8")
|
|
26
|
+
.replace(/^---\n[\s\S]*?\n---\n*/, "")
|
|
27
|
+
.trim();
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** When one skill confidently fits a request, return its name + body so the agent can apply it
|
|
34
|
+
* proactively (instead of merely suggesting it). Null when the match is weak/ambiguous. */
|
|
35
|
+
export function routeConfident(query: string): { name: string; body: string } | null {
|
|
36
|
+
const name = confidentSkill(query, LOADED);
|
|
37
|
+
if (!name) return null;
|
|
38
|
+
const body = skillBody(name);
|
|
39
|
+
return body ? { name, body } : null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function frontName(md: string, fallback: string): string {
|
|
43
|
+
const m = md.match(/^---\n([\s\S]*?)\n---/);
|
|
44
|
+
const name = m?.[1]?.match(/^name:\s*(.+)$/m)?.[1]?.trim() || fallback;
|
|
45
|
+
return name.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "skill";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Install skill(s) from a URL into ~/.ada/skills/. The URL is a SKILL.md, or a JSON index
|
|
49
|
+
* ({ skills: [{ name?, url }] }) of them. Returns the names installed. */
|
|
50
|
+
export async function addRemoteSkill(url: string): Promise<string[]> {
|
|
51
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(20_000) });
|
|
52
|
+
if (!res.ok) throw new Error(`fetch ${url}: HTTP ${res.status}`);
|
|
53
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
54
|
+
const body = await res.text();
|
|
55
|
+
const global = resolve(homedir(), ".ada", "skills");
|
|
56
|
+
const added: string[] = [];
|
|
57
|
+
const write = (md: string, fallback: string): void => {
|
|
58
|
+
const name = frontName(md, fallback);
|
|
59
|
+
const dir = resolve(global, name);
|
|
60
|
+
mkdirSync(dir, { recursive: true });
|
|
61
|
+
const content = md.trimStart().startsWith("---") ? md : `---\nname: ${name}\ndescription: imported skill\ncategory: imported\n---\n\n${md}`;
|
|
62
|
+
writeFileSync(resolve(dir, "SKILL.md"), content);
|
|
63
|
+
added.push(name);
|
|
64
|
+
};
|
|
65
|
+
if (/json/.test(ct) || (body.trimStart().startsWith("{") && !body.trimStart().startsWith("---"))) {
|
|
66
|
+
const idx = JSON.parse(body) as { skills?: { name?: string; url: string }[] };
|
|
67
|
+
for (const s of idx.skills ?? []) {
|
|
68
|
+
try {
|
|
69
|
+
const r = await fetch(new URL(s.url, url), { signal: AbortSignal.timeout(20_000) });
|
|
70
|
+
if (r.ok) write(await r.text(), s.name ?? "skill");
|
|
71
|
+
} catch {
|
|
72
|
+
/* skip a failed entry */
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
write(body, url.split(/[/?#]/).filter(Boolean).pop()?.replace(/\.md$/i, "") ?? "skill");
|
|
77
|
+
}
|
|
78
|
+
return added;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Skills bundled with ada (committed, shipped). src/client/skills.ts → <package>/skills.
|
|
82
|
+
const BUNDLED = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "skills");
|
|
83
|
+
|
|
84
|
+
export interface Skill {
|
|
85
|
+
name: string;
|
|
86
|
+
description: string;
|
|
87
|
+
path: string;
|
|
88
|
+
category?: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function skillDirs(includeProject: boolean): string[] {
|
|
92
|
+
const global = resolve(homedir(), ".ada", "skills");
|
|
93
|
+
// Precedence (first match wins): project → global → bundled built-ins.
|
|
94
|
+
return includeProject ? [resolve(process.cwd(), ".ada", "skills"), global, BUNDLED] : [global, BUNDLED];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Read one `key: value` line from a SKILL.md's `---` front-matter. */
|
|
98
|
+
function frontField(md: string, key: string): string | undefined {
|
|
99
|
+
const m = md.match(/^---\n([\s\S]*?)\n---/);
|
|
100
|
+
return m?.[1]?.match(new RegExp(`^${key}:\\s*(.*)$`, "m"))?.[1]?.trim();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function loadSkills(includeProject: boolean): Skill[] {
|
|
104
|
+
const skills: Skill[] = [];
|
|
105
|
+
for (const dir of skillDirs(includeProject)) {
|
|
106
|
+
if (!existsSync(dir)) continue;
|
|
107
|
+
let entries: string[];
|
|
108
|
+
try {
|
|
109
|
+
entries = readdirSync(dir);
|
|
110
|
+
} catch {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
for (const entry of entries) {
|
|
114
|
+
const nested = join(dir, entry, "SKILL.md");
|
|
115
|
+
const flat = join(dir, entry);
|
|
116
|
+
const file = existsSync(nested) ? nested : entry.endsWith(".md") && existsSync(flat) && statSync(flat).isFile() ? flat : null;
|
|
117
|
+
if (!file) continue;
|
|
118
|
+
const name = entry.replace(/\.md$/, "");
|
|
119
|
+
if (skills.some((s) => s.name === name)) continue; // project wins over global wins over bundled
|
|
120
|
+
try {
|
|
121
|
+
const md = readFileSync(file, "utf8");
|
|
122
|
+
skills.push({ name, description: frontField(md, "description") ?? "(no description)", path: file, category: frontField(md, "category") });
|
|
123
|
+
} catch {
|
|
124
|
+
/* ignore */
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return skills;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Register `list_skills` (browse on demand) + `use_skill` (load one's full instructions). */
|
|
132
|
+
export function registerSkillTool(skills: Skill[]): void {
|
|
133
|
+
if (!skills.length) return;
|
|
134
|
+
LOADED = skills;
|
|
135
|
+
const byName = new Map(skills.map((s) => [s.name, s]));
|
|
136
|
+
const catOf = (s: Skill): string => s.category ?? "other";
|
|
137
|
+
|
|
138
|
+
registerTool({
|
|
139
|
+
name: "find_skill",
|
|
140
|
+
description: "Find the skills most relevant to a task, ranked. Better than list_skills' substring filter for fuzzy matches. Returns the top matches; load one with use_skill.",
|
|
141
|
+
parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"], additionalProperties: false },
|
|
142
|
+
needsApproval: false,
|
|
143
|
+
async run(args) {
|
|
144
|
+
const ranked = routeSkills(String(args.query ?? ""), 8);
|
|
145
|
+
if (!ranked.length) return { output: "No relevant skills found. Try list_skills for the full catalog." };
|
|
146
|
+
return { output: ranked.map((r) => `- ${r.name} — ${r.description}`).join("\n") };
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
registerTool({
|
|
151
|
+
name: "list_skills",
|
|
152
|
+
description:
|
|
153
|
+
"Browse available skills. Optional `category` to list one category, or `filter` substring to search names + descriptions. With no args, returns the categories and their counts. Then load one with use_skill.",
|
|
154
|
+
parameters: { type: "object", properties: { category: { type: "string" }, filter: { type: "string" } }, additionalProperties: false },
|
|
155
|
+
needsApproval: false,
|
|
156
|
+
async run(args) {
|
|
157
|
+
const cat = args.category ? String(args.category).toLowerCase() : "";
|
|
158
|
+
const filt = args.filter ? String(args.filter).toLowerCase() : "";
|
|
159
|
+
if (!cat && !filt) {
|
|
160
|
+
const counts = new Map<string, number>();
|
|
161
|
+
for (const s of skills) counts.set(catOf(s), (counts.get(catOf(s)) ?? 0) + 1);
|
|
162
|
+
const lines = [...counts.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([c, n]) => `${c} (${n})`);
|
|
163
|
+
return { output: `${skills.length} skills across ${counts.size} categories. Pass {category} or {filter} to list them.\n${lines.join(" · ")}` };
|
|
164
|
+
}
|
|
165
|
+
let matched = skills;
|
|
166
|
+
if (cat) matched = matched.filter((s) => catOf(s).toLowerCase() === cat);
|
|
167
|
+
if (filt) matched = matched.filter((s) => `${s.name} ${s.description}`.toLowerCase().includes(filt));
|
|
168
|
+
if (!matched.length) return { output: `No skills match${cat ? ` category=${cat}` : ""}${filt ? ` filter=${filt}` : ""}.` };
|
|
169
|
+
const byCat = new Map<string, Skill[]>();
|
|
170
|
+
for (const s of matched) {
|
|
171
|
+
const c = catOf(s);
|
|
172
|
+
const arr = byCat.get(c) ?? [];
|
|
173
|
+
if (!byCat.has(c)) byCat.set(c, arr);
|
|
174
|
+
arr.push(s);
|
|
175
|
+
}
|
|
176
|
+
const out = [...byCat.entries()]
|
|
177
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
178
|
+
.map(([c, list]) => `## ${c}\n${list.map((s) => `- ${s.name} — ${s.description}`).join("\n")}`)
|
|
179
|
+
.join("\n\n");
|
|
180
|
+
return { output: out };
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
registerTool({
|
|
185
|
+
name: "use_skill",
|
|
186
|
+
description: "Load a skill's full instructions by name before a specialized task. Call list_skills first to see what's available (~200 skills across many categories).",
|
|
187
|
+
parameters: { type: "object", properties: { name: { type: "string" } }, required: ["name"], additionalProperties: false },
|
|
188
|
+
needsApproval: false,
|
|
189
|
+
async run(args) {
|
|
190
|
+
const s = byName.get(String(args.name));
|
|
191
|
+
if (!s) return { output: `Unknown skill: ${String(args.name)}. Call list_skills to see available skills.`, isError: true };
|
|
192
|
+
try {
|
|
193
|
+
return { output: readFileSync(s.path, "utf8") };
|
|
194
|
+
} catch (e) {
|
|
195
|
+
return { output: String(e), isError: true };
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Full-workspace snapshots via git plumbing — capture the entire working tree (tracked + untracked)
|
|
2
|
+
// into a git tree object without touching the user's index, and restore it later. Complements the
|
|
3
|
+
// per-file checkpoint/undo with a whole-tree save point. ponytail: needs a git repo; restore writes
|
|
4
|
+
// snapshotted files back but doesn't delete files created after the snapshot.
|
|
5
|
+
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
import { rmSync } from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
|
|
11
|
+
function git(args: string[], env?: NodeJS.ProcessEnv): { status: number | null; out: string } {
|
|
12
|
+
const r = spawnSync("git", args, { encoding: "utf8", cwd: process.cwd(), env: env ?? process.env });
|
|
13
|
+
return { status: r.status, out: (r.stdout ?? "").trim() };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let last: string | null = null;
|
|
17
|
+
|
|
18
|
+
/** Snapshot the full working tree into a git tree object; returns its SHA (or null if not a repo). */
|
|
19
|
+
export function snapshot(): string | null {
|
|
20
|
+
const idx = join(tmpdir(), `ada-snap-${process.pid}-${Date.now()}`);
|
|
21
|
+
const env = { ...process.env, GIT_INDEX_FILE: idx };
|
|
22
|
+
try {
|
|
23
|
+
if (git(["add", "-A", "."], env).status !== 0) return null;
|
|
24
|
+
const tree = git(["write-tree"], env).out || null;
|
|
25
|
+
if (tree) last = tree;
|
|
26
|
+
return tree;
|
|
27
|
+
} finally {
|
|
28
|
+
try {
|
|
29
|
+
rmSync(idx, { force: true });
|
|
30
|
+
} catch {
|
|
31
|
+
/* best-effort */
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Restore a snapshot tree (or the last one taken) into the working tree. */
|
|
37
|
+
export function restore(tree?: string): boolean {
|
|
38
|
+
const t = tree ?? last;
|
|
39
|
+
if (!t) return false;
|
|
40
|
+
const idx = join(tmpdir(), `ada-snap-${process.pid}-${Date.now()}`);
|
|
41
|
+
const env = { ...process.env, GIT_INDEX_FILE: idx };
|
|
42
|
+
try {
|
|
43
|
+
if (git(["read-tree", t], env).status !== 0) return false;
|
|
44
|
+
return git(["checkout-index", "-a", "-f"], env).status === 0;
|
|
45
|
+
} finally {
|
|
46
|
+
try {
|
|
47
|
+
rmSync(idx, { force: true });
|
|
48
|
+
} catch {
|
|
49
|
+
/* best-effort */
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function hasSnapshot(): boolean {
|
|
55
|
+
return last !== null;
|
|
56
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Lightweight, opt-out telemetry. Events append to ~/.ada/telemetry.jsonl, and (if
|
|
2
|
+
// ADA_OTLP_ENDPOINT is set) are also POSTed there as JSON. Disable with ADA_TELEMETRY=0.
|
|
3
|
+
|
|
4
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
|
|
8
|
+
const FILE = join(homedir(), ".ada", "telemetry.jsonl");
|
|
9
|
+
const ENABLED = process.env.ADA_TELEMETRY !== "0";
|
|
10
|
+
const OTLP = process.env.ADA_OTLP_ENDPOINT;
|
|
11
|
+
|
|
12
|
+
export function track(event: string, data: Record<string, unknown> = {}): void {
|
|
13
|
+
if (!ENABLED) return;
|
|
14
|
+
const rec = { ts: new Date().toISOString(), event, ...data };
|
|
15
|
+
try {
|
|
16
|
+
mkdirSync(dirname(FILE), { recursive: true });
|
|
17
|
+
appendFileSync(FILE, `${JSON.stringify(rec)}\n`, "utf8");
|
|
18
|
+
} catch {
|
|
19
|
+
/* best-effort */
|
|
20
|
+
}
|
|
21
|
+
if (OTLP) {
|
|
22
|
+
fetch(OTLP, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(rec) }).catch(() => undefined);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// A task checklist the agent maintains via the update_todos tool, rendered like pi's todo view.
|
|
2
|
+
|
|
3
|
+
export interface Todo {
|
|
4
|
+
text: string;
|
|
5
|
+
status: "todo" | "doing" | "done";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let current: Todo[] = [];
|
|
9
|
+
|
|
10
|
+
export function setTodos(items: Todo[]): void {
|
|
11
|
+
current = items;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getTodos(): Todo[] {
|
|
15
|
+
return current;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function renderTodos(): string {
|
|
19
|
+
if (!current.length) return "\x1b[2m(no todos)\x1b[0m";
|
|
20
|
+
const mark = (s: Todo["status"]): string =>
|
|
21
|
+
s === "done" ? "\x1b[32m✓\x1b[0m" : s === "doing" ? "\x1b[38;5;214m▸\x1b[0m" : "\x1b[2m○\x1b[0m";
|
|
22
|
+
return current.map((t) => ` ${mark(t.status)} ${t.status === "done" ? `\x1b[2m${t.text}\x1b[0m` : t.text}`).join("\n");
|
|
23
|
+
}
|