@lazyingart/agintiflow 0.13.0 → 0.14.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/README.md +10 -0
- package/docs/skills-and-tools.md +58 -0
- package/package.json +5 -2
- package/scripts/smoke-capabilities.js +9 -1
- package/scripts/smoke-cli-chat.js +5 -1
- package/scripts/smoke-skills.js +70 -0
- package/scripts/smoke-web-api.js +3 -0
- package/skills/aaps/SKILL.md +21 -0
- package/skills/android/SKILL.md +23 -0
- package/skills/book-writing/SKILL.md +23 -0
- package/skills/c-cpp/SKILL.md +23 -0
- package/skills/code/SKILL.md +25 -0
- package/skills/github-maintenance/SKILL.md +23 -0
- package/skills/image-generation/SKILL.md +23 -0
- package/skills/latex-manuscript/SKILL.md +25 -0
- package/skills/novel-writing/SKILL.md +21 -0
- package/skills/python/SKILL.md +24 -0
- package/skills/r-stan/SKILL.md +24 -0
- package/skills/shell/SKILL.md +21 -0
- package/skills/system-maintenance/SKILL.md +24 -0
- package/skills/website-app/SKILL.md +26 -0
- package/skills/word-documents/SKILL.md +22 -0
- package/src/agent-runner.js +10 -0
- package/src/capabilities.js +8 -0
- package/src/cli.js +28 -1
- package/src/interactive-cli.js +28 -0
- package/src/model-client.js +4 -0
- package/src/skill-library.js +153 -0
- package/web.js +2 -0
package/README.md
CHANGED
|
@@ -83,6 +83,16 @@ For current docs, install errors, package/toolchain setup, and source discovery,
|
|
|
83
83
|
|
|
84
84
|
For raster image work, AgInTiFlow has an optional `image_generation` skill backed by the `generate_image` tool and a local `GRSAI` key. The skill tells DeepSeek when image generation is appropriate; the tool calls GRS AI Nano Banana, saves manifests/images under `artifacts/images`, and sends the result to the canvas. See [docs/auxiliary-image-generation.md](docs/auxiliary-image-generation.md).
|
|
85
85
|
|
|
86
|
+
AgInTiFlow now ships a Markdown skill library in `skills/<id>/SKILL.md`. Skills are prompt playbooks, while tools are deterministic actions such as `apply_patch`, `run_command`, `web_search`, `generate_image`, and `send_to_canvas`. Built-in skills cover code, websites/apps, LaTeX manuscripts, books, Word documents, image generation, GitHub, system maintenance, Android, R/Stan, Python, C/C++, shell, AAPS, and novel writing. See [docs/skills-and-tools.md](docs/skills-and-tools.md).
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
aginti skills
|
|
90
|
+
aginti skills website
|
|
91
|
+
aginti --list-skills latex
|
|
92
|
+
# in chat:
|
|
93
|
+
/skills github commit
|
|
94
|
+
```
|
|
95
|
+
|
|
86
96
|
Launch the local web UI from an installed package:
|
|
87
97
|
|
|
88
98
|
```bash
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Skills And Tools
|
|
2
|
+
|
|
3
|
+
AgInTiFlow separates **skills** from **tools** so the agent can stay general while still improving on specialized work.
|
|
4
|
+
|
|
5
|
+
## Definitions
|
|
6
|
+
|
|
7
|
+
**Skill**: Markdown guidance stored at `skills/<id>/SKILL.md`. A skill describes when to use a workflow, what to inspect first, which outputs matter, and which tools are usually useful. Skills are prompt context, not executable code.
|
|
8
|
+
|
|
9
|
+
**Tool**: A deterministic callable capability exposed to the model, such as `inspect_project`, `read_file`, `apply_patch`, `run_command`, `web_search`, `generate_image`, `preview_workspace`, or `send_to_canvas`.
|
|
10
|
+
|
|
11
|
+
**Profile**: A broad runtime mode such as `auto`, `code`, `latex`, or `maintenance`. Profiles tune routing, max steps, and general behavior. Skills can combine across profiles.
|
|
12
|
+
|
|
13
|
+
## Built-In Skills
|
|
14
|
+
|
|
15
|
+
The package ships built-in skills for code engineering, website/app building, LaTeX manuscripts, books, Microsoft Word documents, image generation, GitHub maintenance, system maintenance, Android, R/Stan, Python, C/C++, shell scripting, AAPS, and novel writing.
|
|
16
|
+
|
|
17
|
+
List them from a project:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
aginti skills
|
|
21
|
+
aginti skills website
|
|
22
|
+
aginti --list-skills latex
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Inside interactive chat:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
/skills
|
|
29
|
+
/skills github commit
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Selection Flow
|
|
33
|
+
|
|
34
|
+
For every run, AgInTiFlow scores the user goal and active profile against skill frontmatter:
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
---
|
|
38
|
+
id: latex-manuscript
|
|
39
|
+
label: LaTeX Manuscript
|
|
40
|
+
description: Write, compile, and package LaTeX papers, reports, figures, bibliographies, and PDFs.
|
|
41
|
+
triggers:
|
|
42
|
+
- latex
|
|
43
|
+
- tex
|
|
44
|
+
- manuscript
|
|
45
|
+
tools:
|
|
46
|
+
- write_file
|
|
47
|
+
- apply_patch
|
|
48
|
+
- run_command
|
|
49
|
+
---
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Selected skills are injected into the plan and execution prompts. The LLM still decides what to do; skills only provide domain playbooks and guardrails.
|
|
53
|
+
|
|
54
|
+
## Adding A Skill
|
|
55
|
+
|
|
56
|
+
Create `skills/<id>/SKILL.md` with valid YAML frontmatter and a short Markdown body. Keep descriptions strings, not YAML arrays, because loaders expect `id`, `label`, and `description` as scalar strings.
|
|
57
|
+
|
|
58
|
+
Good skills are small, actionable, and tool-aware. They should say what to inspect, what to create or verify, and what to avoid. They should not hard-code one exact task.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a resumable Playwright website-control agent with OpenAI-compatible tool calling.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -46,10 +46,12 @@
|
|
|
46
46
|
"scripts/smoke-coding-tools.js",
|
|
47
47
|
"scripts/smoke-capabilities.js",
|
|
48
48
|
"scripts/smoke-inbox.js",
|
|
49
|
+
"scripts/smoke-skills.js",
|
|
49
50
|
"scripts/smoke-toolchain-docker.js",
|
|
50
51
|
"scripts/smoke-web-api.js",
|
|
51
52
|
"src/",
|
|
52
53
|
"run.js",
|
|
54
|
+
"skills/",
|
|
53
55
|
"web.js"
|
|
54
56
|
],
|
|
55
57
|
"bin": {
|
|
@@ -64,11 +66,12 @@
|
|
|
64
66
|
"smoke:coding-tools": "node scripts/smoke-coding-tools.js",
|
|
65
67
|
"smoke:auxiliary-tools": "node scripts/smoke-auxiliary-tools.js",
|
|
66
68
|
"smoke:cli-chat": "node scripts/smoke-cli-chat.js",
|
|
69
|
+
"smoke:skills": "node scripts/smoke-skills.js",
|
|
67
70
|
"smoke:toolchain-docker": "node scripts/smoke-toolchain-docker.js",
|
|
68
71
|
"smoke:inbox": "node scripts/smoke-inbox.js",
|
|
69
72
|
"smoke:web-api": "node scripts/smoke-web-api.js",
|
|
70
73
|
"real:deepseek": "node scripts/real-deepseek-capabilities.js",
|
|
71
|
-
"test": "npm run check && npm run smoke:web-api && npm run smoke:coding-tools && npm run smoke:auxiliary-tools && npm run smoke:capabilities && npm run smoke:cli-chat && npm run smoke:inbox",
|
|
74
|
+
"test": "npm run check && npm run smoke:web-api && npm run smoke:coding-tools && npm run smoke:auxiliary-tools && npm run smoke:capabilities && npm run smoke:skills && npm run smoke:cli-chat && npm run smoke:inbox",
|
|
72
75
|
"pack:dry-run": "npm pack --dry-run",
|
|
73
76
|
"smoke:capabilities": "node scripts/smoke-capabilities.js"
|
|
74
77
|
},
|
|
@@ -81,6 +81,14 @@ try {
|
|
|
81
81
|
capabilities.trustedDockerPolicy.some((check) => check.command.startsWith("chmod") && check.allowed),
|
|
82
82
|
"trusted Docker policy did not allow chmod"
|
|
83
83
|
);
|
|
84
|
+
assert(
|
|
85
|
+
capabilities.tools?.skills?.some((skill) => skill.id === "website-app"),
|
|
86
|
+
"capabilities did not report built-in website skill"
|
|
87
|
+
);
|
|
88
|
+
assert(
|
|
89
|
+
capabilities.tools?.skills?.some((skill) => skill.id === "latex-manuscript"),
|
|
90
|
+
"capabilities did not report built-in LaTeX skill"
|
|
91
|
+
);
|
|
84
92
|
|
|
85
93
|
const doctor = JSON.parse(await runCli(["doctor", "--capabilities", "--json"]));
|
|
86
94
|
assert(doctor.project.root === tempRoot, "doctor --capabilities used the wrong project root");
|
|
@@ -91,7 +99,7 @@ try {
|
|
|
91
99
|
{
|
|
92
100
|
ok: true,
|
|
93
101
|
projectRoot: tempRoot,
|
|
94
|
-
checks: ["aginti-md-init", "capabilities-cli", "doctor-capabilities", "maintenance-policy", "trusted-docker-policy", "git-policy"],
|
|
102
|
+
checks: ["aginti-md-init", "capabilities-cli", "doctor-capabilities", "maintenance-policy", "trusted-docker-policy", "git-policy", "skills-capability"],
|
|
95
103
|
},
|
|
96
104
|
null,
|
|
97
105
|
2
|
|
@@ -122,6 +122,10 @@ try {
|
|
|
122
122
|
if (!instructionsResult.stdout.includes("AGINTI.md") || !instructionsResult.stdout.includes("Project instructions")) {
|
|
123
123
|
throw new Error("interactive /instructions did not show AGINTI.md");
|
|
124
124
|
}
|
|
125
|
+
const skillsResult = await runChat("/skills website\n/exit\n");
|
|
126
|
+
if (!skillsResult.stdout.includes("website-app") || !skillsResult.stdout.includes("Website And App Builder")) {
|
|
127
|
+
throw new Error("interactive /skills did not show matching built-in skills");
|
|
128
|
+
}
|
|
125
129
|
await runChat("remember that this project prefers pytest smoke tests in AGINTI.md\n/exit\n");
|
|
126
130
|
const updatedInstructions = await fs.readFile(path.join(tempRoot, "AGINTI.md"), "utf8");
|
|
127
131
|
if (!updatedInstructions.includes("pytest smoke tests")) {
|
|
@@ -159,7 +163,7 @@ try {
|
|
|
159
163
|
{
|
|
160
164
|
ok: true,
|
|
161
165
|
projectRoot: tempRoot,
|
|
162
|
-
checks: ["markdown-render", "markdown-table-no-duplicate", "patch-diff-render", "prompt-layout", "live-input-status-layout", "agent-response-gutter", "aginti-md", "instructions-command", "instructions-chat-edit", "interactive-chat", "mock-file-write", "run-status", "resume-latest", "resume-history-full"],
|
|
166
|
+
checks: ["markdown-render", "markdown-table-no-duplicate", "patch-diff-render", "prompt-layout", "live-input-status-layout", "agent-response-gutter", "aginti-md", "instructions-command", "skills-command", "instructions-chat-edit", "interactive-chat", "mock-file-write", "run-status", "resume-latest", "resume-history-full"],
|
|
163
167
|
},
|
|
164
168
|
null,
|
|
165
169
|
2
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { formatSkillsForPrompt, listSkills, selectSkillsForGoal } from "../src/skill-library.js";
|
|
7
|
+
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
10
|
+
|
|
11
|
+
function assert(condition, message) {
|
|
12
|
+
if (!condition) throw new Error(message);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function selectedIds(goal, taskProfile = "auto") {
|
|
16
|
+
return selectSkillsForGoal(goal, { taskProfile, limit: 8 }).map((skill) => skill.id);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const skills = listSkills({ includeBody: true });
|
|
20
|
+
const ids = new Set(skills.map((skill) => skill.id));
|
|
21
|
+
assert(skills.length >= 14, "expected built-in skills to load");
|
|
22
|
+
for (const required of [
|
|
23
|
+
"aaps",
|
|
24
|
+
"code",
|
|
25
|
+
"github-maintenance",
|
|
26
|
+
"image-generation",
|
|
27
|
+
"latex-manuscript",
|
|
28
|
+
"system-maintenance",
|
|
29
|
+
"website-app",
|
|
30
|
+
"word-documents",
|
|
31
|
+
]) {
|
|
32
|
+
assert(ids.has(required), `missing required skill ${required}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
assert(selectedIds("write a beautiful React website and preview it").includes("website-app"), "website prompt did not select website-app");
|
|
36
|
+
assert(selectedIds("write a LaTeX paper and compile a PDF").includes("latex-manuscript"), "latex prompt did not select latex-manuscript");
|
|
37
|
+
assert(selectedIds("edit a Microsoft Word docx and preserve the original").includes("word-documents"), "docx prompt did not select word-documents");
|
|
38
|
+
assert(selectedIds("generate a logo image with grsai nanobanana").includes("image-generation"), "image prompt did not select image-generation");
|
|
39
|
+
assert(selectedIds("git status commit push with gh").includes("github-maintenance"), "git prompt did not select github-maintenance");
|
|
40
|
+
assert(selectedIds("create an .aaps example for @lazyingart/aaps").includes("aaps"), "AAPS prompt did not select aaps");
|
|
41
|
+
assert(selectedIds("debug a C++ CMake build").includes("c-cpp"), "C++ prompt did not select c-cpp");
|
|
42
|
+
assert(selectedIds("set up Stan and CmdStanR reproducibly").includes("r-stan"), "Stan prompt did not select r-stan");
|
|
43
|
+
|
|
44
|
+
const prompt = formatSkillsForPrompt(selectSkillsForGoal("write latex manuscript with figures", { taskProfile: "latex", limit: 3 }));
|
|
45
|
+
assert(prompt.includes("A skill is Markdown guidance"), "skill prompt does not explain skill semantics");
|
|
46
|
+
assert(prompt.includes("latex-manuscript"), "skill prompt omitted selected skill");
|
|
47
|
+
assert(prompt.length < 5400, "skill prompt is too large for normal runs");
|
|
48
|
+
|
|
49
|
+
const cli = await execFileAsync(process.execPath, [path.join(repoRoot, "bin/aginti-cli.js"), "skills", "website"], {
|
|
50
|
+
cwd: repoRoot,
|
|
51
|
+
timeout: 10000,
|
|
52
|
+
maxBuffer: 512 * 1024,
|
|
53
|
+
env: {
|
|
54
|
+
...process.env,
|
|
55
|
+
AGINTIFLOW_RUNTIME_DIR: "",
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
assert(cli.stdout.includes("website-app"), "aginti skills website did not print website-app");
|
|
59
|
+
|
|
60
|
+
console.log(
|
|
61
|
+
JSON.stringify(
|
|
62
|
+
{
|
|
63
|
+
ok: true,
|
|
64
|
+
skills: skills.length,
|
|
65
|
+
checks: ["load-built-ins", "select-by-goal", "prompt-format", "cli-skills"],
|
|
66
|
+
},
|
|
67
|
+
null,
|
|
68
|
+
2
|
|
69
|
+
)
|
|
70
|
+
);
|
package/scripts/smoke-web-api.js
CHANGED
|
@@ -81,6 +81,9 @@ try {
|
|
|
81
81
|
if (!Array.isArray(config.taskProfiles) || !config.taskProfiles.some((profile) => profile.id === "latex")) {
|
|
82
82
|
throw new Error("task profiles are not advertised by /api/config");
|
|
83
83
|
}
|
|
84
|
+
if (!Array.isArray(config.skills) || !config.skills.some((skill) => skill.id === "website-app")) {
|
|
85
|
+
throw new Error("built-in skills are not advertised by /api/config");
|
|
86
|
+
}
|
|
84
87
|
|
|
85
88
|
const keyStatus = await fetchJson("/api/keys/status");
|
|
86
89
|
if (typeof keyStatus.keyStatus?.deepseek !== "boolean") throw new Error("key status endpoint is invalid");
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: aaps
|
|
3
|
+
label: AAPS Workflows
|
|
4
|
+
description: Work with ../AAPS, .aaps files, @lazyingart/aaps, automation plans, and related project conventions.
|
|
5
|
+
triggers:
|
|
6
|
+
- aaps
|
|
7
|
+
- .aaps
|
|
8
|
+
- @lazyingart/aaps
|
|
9
|
+
- automation plan
|
|
10
|
+
tools:
|
|
11
|
+
- inspect_project
|
|
12
|
+
- read_file
|
|
13
|
+
- write_file
|
|
14
|
+
- run_command
|
|
15
|
+
- web_search
|
|
16
|
+
---
|
|
17
|
+
# AAPS Workflows
|
|
18
|
+
|
|
19
|
+
Inspect nearby `.aaps` files, package docs, and project notes before generating workflows. Keep assumptions explicit and outputs project-local.
|
|
20
|
+
|
|
21
|
+
Do not publish, upload, or expose credentials unless explicitly requested and safe.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: android
|
|
3
|
+
label: Android Development
|
|
4
|
+
description: Build and debug Android, Gradle, Kotlin, Java, emulator, and mobile app projects.
|
|
5
|
+
triggers:
|
|
6
|
+
- android
|
|
7
|
+
- gradle
|
|
8
|
+
- kotlin
|
|
9
|
+
- java
|
|
10
|
+
- apk
|
|
11
|
+
- emulator
|
|
12
|
+
tools:
|
|
13
|
+
- inspect_project
|
|
14
|
+
- search_files
|
|
15
|
+
- apply_patch
|
|
16
|
+
- run_command
|
|
17
|
+
- web_search
|
|
18
|
+
---
|
|
19
|
+
# Android Development
|
|
20
|
+
|
|
21
|
+
Inspect Gradle files, manifests, package names, modules, and existing build scripts before editing. Prefer narrow Gradle tasks and source-set-aware patches.
|
|
22
|
+
|
|
23
|
+
If SDK/emulator tooling is missing, produce a setup report or project-local script rather than guessing.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: book-writing
|
|
3
|
+
label: Book And Long-Form Writing
|
|
4
|
+
description: Plan, draft, revise, and structure books, chapters, scripts, tutorials, and long documents.
|
|
5
|
+
triggers:
|
|
6
|
+
- book
|
|
7
|
+
- chapter
|
|
8
|
+
- script
|
|
9
|
+
- tutorial
|
|
10
|
+
- long form
|
|
11
|
+
- outline
|
|
12
|
+
- manuscript
|
|
13
|
+
tools:
|
|
14
|
+
- write_file
|
|
15
|
+
- read_file
|
|
16
|
+
- web_search
|
|
17
|
+
- send_to_canvas
|
|
18
|
+
---
|
|
19
|
+
# Book And Long-Form Writing
|
|
20
|
+
|
|
21
|
+
Start with structure: audience, promise, outline, chapter flow, voice, and revision plan. Save durable drafts as Markdown unless the user requests another format.
|
|
22
|
+
|
|
23
|
+
Use web search for current facts and keep source notes when claims matter. For long work, produce incremental chapters and revision notes instead of one huge brittle output.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: c-cpp
|
|
3
|
+
label: C And C++ Development
|
|
4
|
+
description: Work with C, C++, CMake, Make, native builds, tests, and low-level debugging.
|
|
5
|
+
triggers:
|
|
6
|
+
- c++
|
|
7
|
+
- cpp
|
|
8
|
+
- c language
|
|
9
|
+
- cmake
|
|
10
|
+
- makefile
|
|
11
|
+
- gcc
|
|
12
|
+
- clang
|
|
13
|
+
tools:
|
|
14
|
+
- inspect_project
|
|
15
|
+
- search_files
|
|
16
|
+
- apply_patch
|
|
17
|
+
- run_command
|
|
18
|
+
---
|
|
19
|
+
# C And C++ Development
|
|
20
|
+
|
|
21
|
+
Inspect build files before source edits. Keep patches narrow, respect formatting style, and run the smallest compile/test target available.
|
|
22
|
+
|
|
23
|
+
If native dependencies are missing, prefer Docker/project-local setup notes and do not silently change host toolchains.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: code
|
|
3
|
+
label: Code Engineering
|
|
4
|
+
description: Build, edit, debug, test, and maintain software projects across languages.
|
|
5
|
+
triggers:
|
|
6
|
+
- code
|
|
7
|
+
- app
|
|
8
|
+
- bug
|
|
9
|
+
- test
|
|
10
|
+
- refactor
|
|
11
|
+
- package
|
|
12
|
+
- project
|
|
13
|
+
tools:
|
|
14
|
+
- inspect_project
|
|
15
|
+
- search_files
|
|
16
|
+
- read_file
|
|
17
|
+
- apply_patch
|
|
18
|
+
- run_command
|
|
19
|
+
- web_search
|
|
20
|
+
---
|
|
21
|
+
# Code Engineering
|
|
22
|
+
|
|
23
|
+
Orient before editing: inspect the project map, read instructions/manifests, search exact symbols or errors, then patch the smallest coherent set of files.
|
|
24
|
+
|
|
25
|
+
Prefer deterministic `apply_patch` for source edits. Run focused checks first, repair failures, then broaden checks only when useful. Summarize changed files, commands run, and residual risk.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: github-maintenance
|
|
3
|
+
label: GitHub And Release Maintenance
|
|
4
|
+
description: Use git and gh safely for status, commits, pull requests, releases, and repository maintenance.
|
|
5
|
+
triggers:
|
|
6
|
+
- git
|
|
7
|
+
- github
|
|
8
|
+
- gh
|
|
9
|
+
- commit
|
|
10
|
+
- push
|
|
11
|
+
- pull request
|
|
12
|
+
- release
|
|
13
|
+
tools:
|
|
14
|
+
- run_command
|
|
15
|
+
- read_file
|
|
16
|
+
- apply_patch
|
|
17
|
+
- web_search
|
|
18
|
+
---
|
|
19
|
+
# GitHub And Release Maintenance
|
|
20
|
+
|
|
21
|
+
Always inspect `git status --short` and relevant diffs before committing or pushing. Keep commits scoped and stop on conflicts, divergence, or unrelated dirty work.
|
|
22
|
+
|
|
23
|
+
Use `gh` for PR/release/status workflows when authenticated. Fold long command output but preserve key errors, URLs, branch names, and commit hashes.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: image-generation
|
|
3
|
+
label: Image Generation
|
|
4
|
+
description: Generate or edit raster images, posters, covers, logos, illustrations, and visual assets.
|
|
5
|
+
triggers:
|
|
6
|
+
- image
|
|
7
|
+
- logo
|
|
8
|
+
- poster
|
|
9
|
+
- cover
|
|
10
|
+
- illustration
|
|
11
|
+
- nanobanana
|
|
12
|
+
- grsai
|
|
13
|
+
- gpt image
|
|
14
|
+
tools:
|
|
15
|
+
- generate_image
|
|
16
|
+
- write_file
|
|
17
|
+
- send_to_canvas
|
|
18
|
+
---
|
|
19
|
+
# Image Generation
|
|
20
|
+
|
|
21
|
+
Use image generation when the user asks for a raster visual asset or prompt. Write a concise prompt with subject, style, composition, color, lighting, and output constraints.
|
|
22
|
+
|
|
23
|
+
Prefer `generate_image` when a GRS AI key is available. Save selected outputs under artifacts and send the chosen image to canvas.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: latex-manuscript
|
|
3
|
+
label: LaTeX Manuscript
|
|
4
|
+
description: Write, compile, and package LaTeX papers, reports, figures, bibliographies, and PDFs.
|
|
5
|
+
triggers:
|
|
6
|
+
- latex
|
|
7
|
+
- tex
|
|
8
|
+
- manuscript
|
|
9
|
+
- paper
|
|
10
|
+
- pdf
|
|
11
|
+
- figure
|
|
12
|
+
- overleaf
|
|
13
|
+
tools:
|
|
14
|
+
- write_file
|
|
15
|
+
- apply_patch
|
|
16
|
+
- run_command
|
|
17
|
+
- open_workspace_file
|
|
18
|
+
- send_to_canvas
|
|
19
|
+
- web_search
|
|
20
|
+
---
|
|
21
|
+
# LaTeX Manuscript
|
|
22
|
+
|
|
23
|
+
Use a project subfolder with `main.tex`, figures, bibliography, and generated PDFs. Compile from the correct directory with `latexmk` or `pdflatex` when available.
|
|
24
|
+
|
|
25
|
+
If TeX is missing, produce an honest setup note or Docker-local setup plan instead of faking success. Send the final PDF or source to canvas when useful.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: novel-writing
|
|
3
|
+
label: Novel Writing
|
|
4
|
+
description: Plan, draft, revise, and manage novels, character arcs, scenes, chapters, and long fiction.
|
|
5
|
+
triggers:
|
|
6
|
+
- novel
|
|
7
|
+
- fiction
|
|
8
|
+
- character
|
|
9
|
+
- scene
|
|
10
|
+
- plot
|
|
11
|
+
- chapter
|
|
12
|
+
tools:
|
|
13
|
+
- write_file
|
|
14
|
+
- read_file
|
|
15
|
+
- send_to_canvas
|
|
16
|
+
---
|
|
17
|
+
# Novel Writing
|
|
18
|
+
|
|
19
|
+
Track premise, characters, setting, conflict, tone, and continuity. Save outlines, chapter drafts, and revision notes as durable files.
|
|
20
|
+
|
|
21
|
+
For long works, write in scenes or chapters and maintain a compact continuity bible instead of relying on chat history alone.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: python
|
|
3
|
+
label: Python Development
|
|
4
|
+
description: Build Python packages, CLIs, scripts, tests, notebooks, plotting, and data workflows.
|
|
5
|
+
triggers:
|
|
6
|
+
- python
|
|
7
|
+
- pytest
|
|
8
|
+
- pyproject
|
|
9
|
+
- pip
|
|
10
|
+
- uv
|
|
11
|
+
- matplotlib
|
|
12
|
+
- pandas
|
|
13
|
+
tools:
|
|
14
|
+
- inspect_project
|
|
15
|
+
- search_files
|
|
16
|
+
- apply_patch
|
|
17
|
+
- run_command
|
|
18
|
+
- send_to_canvas
|
|
19
|
+
---
|
|
20
|
+
# Python Development
|
|
21
|
+
|
|
22
|
+
Inspect `pyproject.toml`, requirements, package layout, and tests. Prefer stdlib tests when dependency installs are unnecessary; otherwise use project-local venv/conda/uv or Docker.
|
|
23
|
+
|
|
24
|
+
For plots and artifacts, save files with clear names and send useful outputs to canvas.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: r-stan
|
|
3
|
+
label: R And Stan
|
|
4
|
+
description: Work with R, Stan, CmdStan, CmdStanR, PyStan, statistics, and reproducible analysis projects.
|
|
5
|
+
triggers:
|
|
6
|
+
- r
|
|
7
|
+
- stan
|
|
8
|
+
- cmdstan
|
|
9
|
+
- cmdstanr
|
|
10
|
+
- pystan
|
|
11
|
+
- statistics
|
|
12
|
+
- bayesian
|
|
13
|
+
tools:
|
|
14
|
+
- inspect_project
|
|
15
|
+
- write_file
|
|
16
|
+
- run_command
|
|
17
|
+
- web_search
|
|
18
|
+
- send_to_canvas
|
|
19
|
+
---
|
|
20
|
+
# R And Stan
|
|
21
|
+
|
|
22
|
+
Keep analysis reproducible: scripts, data paths, package notes, seed handling, and output folders. Prefer Docker or project-local toolchains for installation.
|
|
23
|
+
|
|
24
|
+
For Stan, validate model syntax and compile/run only when toolchains exist; otherwise write honest setup scripts and checks.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: shell
|
|
3
|
+
label: Shell Scripting
|
|
4
|
+
description: Write, audit, and run shell scripts for automation, setup, diagnostics, and maintenance.
|
|
5
|
+
triggers:
|
|
6
|
+
- shell
|
|
7
|
+
- bash
|
|
8
|
+
- script
|
|
9
|
+
- terminal
|
|
10
|
+
- command
|
|
11
|
+
- cli
|
|
12
|
+
tools:
|
|
13
|
+
- write_file
|
|
14
|
+
- run_command
|
|
15
|
+
- web_search
|
|
16
|
+
---
|
|
17
|
+
# Shell Scripting
|
|
18
|
+
|
|
19
|
+
Prefer idempotent scripts with `set -euo pipefail` when appropriate, clear variables, dry-run or validation modes, and `bash -n` checks.
|
|
20
|
+
|
|
21
|
+
Separate diagnosis from mutation. Keep dangerous host operations explicit and reversible.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: system-maintenance
|
|
3
|
+
label: System Maintenance
|
|
4
|
+
description: Diagnose and repair local system, shell, package, Docker, conda, Python, R, and toolchain problems.
|
|
5
|
+
triggers:
|
|
6
|
+
- system
|
|
7
|
+
- install
|
|
8
|
+
- package
|
|
9
|
+
- docker
|
|
10
|
+
- conda
|
|
11
|
+
- environment
|
|
12
|
+
- fix computer
|
|
13
|
+
- error
|
|
14
|
+
tools:
|
|
15
|
+
- run_command
|
|
16
|
+
- write_file
|
|
17
|
+
- web_search
|
|
18
|
+
- send_to_canvas
|
|
19
|
+
---
|
|
20
|
+
# System Maintenance
|
|
21
|
+
|
|
22
|
+
Diagnose first with read-only commands. Prefer Docker/project-local scripts and `/aginti-env` for broad toolchain setup. Host-level sudo or destructive operations require explicit user approval outside normal automation.
|
|
23
|
+
|
|
24
|
+
For complicated setup, write idempotent scripts, run syntax checks, and report exact next safe command.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: website-app
|
|
3
|
+
label: Website And App Builder
|
|
4
|
+
description: Build polished websites, web apps, landing pages, dashboards, and local previews.
|
|
5
|
+
triggers:
|
|
6
|
+
- website
|
|
7
|
+
- web app
|
|
8
|
+
- landing page
|
|
9
|
+
- frontend
|
|
10
|
+
- dashboard
|
|
11
|
+
- react
|
|
12
|
+
- html
|
|
13
|
+
tools:
|
|
14
|
+
- inspect_project
|
|
15
|
+
- write_file
|
|
16
|
+
- apply_patch
|
|
17
|
+
- run_command
|
|
18
|
+
- preview_workspace
|
|
19
|
+
- open_workspace_file
|
|
20
|
+
- send_to_canvas
|
|
21
|
+
---
|
|
22
|
+
# Website And App Builder
|
|
23
|
+
|
|
24
|
+
Create real files, not only advice. For new UI, choose a clear visual direction, responsive layout, purposeful typography, and meaningful content. For existing apps, preserve conventions.
|
|
25
|
+
|
|
26
|
+
Use package scripts when available. Preview with `preview_workspace` or `open_workspace_file`; avoid transient Docker localhost loops.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: word-documents
|
|
3
|
+
label: Microsoft Word Documents
|
|
4
|
+
description: Edit, convert, inspect, or generate Word-style documents using available local tools.
|
|
5
|
+
triggers:
|
|
6
|
+
- word
|
|
7
|
+
- docx
|
|
8
|
+
- microsoft word
|
|
9
|
+
- office
|
|
10
|
+
- pandoc
|
|
11
|
+
- libreoffice
|
|
12
|
+
tools:
|
|
13
|
+
- read_file
|
|
14
|
+
- write_file
|
|
15
|
+
- run_command
|
|
16
|
+
- send_to_canvas
|
|
17
|
+
---
|
|
18
|
+
# Microsoft Word Documents
|
|
19
|
+
|
|
20
|
+
Prefer safe conversions through available tools such as `pandoc`, `libreoffice`, or Python libraries when installed. Preserve originals; write converted or edited outputs to a new file unless overwrite is explicit.
|
|
21
|
+
|
|
22
|
+
If binary `.docx` content cannot be inspected directly, explain the needed converter and create a project-local script or setup note.
|
package/src/agent-runner.js
CHANGED
|
@@ -22,6 +22,7 @@ import { engineeringGuidanceForTask } from "./engineering-guidance.js";
|
|
|
22
22
|
import { searchWeb } from "./web-search.js";
|
|
23
23
|
import { runParallelScouts, shouldRunParallelScouts } from "./parallel-scouts.js";
|
|
24
24
|
import { readProjectInstructions } from "./project.js";
|
|
25
|
+
import { formatSkillsForPrompt, selectSkillsForGoal } from "./skill-library.js";
|
|
25
26
|
|
|
26
27
|
const exec = promisify(execCallback);
|
|
27
28
|
const BROWSER_TOOLS = new Set(["open_url", "open_workspace_file", "preview_workspace", "click", "type", "scroll", "press", "back"]);
|
|
@@ -252,6 +253,8 @@ async function createInitialState(config, sessionId) {
|
|
|
252
253
|
const now = new Date().toISOString();
|
|
253
254
|
const taskProfile = getTaskProfile(config.taskProfile);
|
|
254
255
|
const engineeringGuidance = engineeringGuidanceForTask(config.goal, config.taskProfile);
|
|
256
|
+
const selectedSkills = selectSkillsForGoal(config.goal, { taskProfile: config.taskProfile, limit: 6 });
|
|
257
|
+
const skillContext = formatSkillsForPrompt(selectedSkills);
|
|
255
258
|
const projectInstructions = await readProjectInstructions(config.baseDir || config.commandCwd || process.cwd());
|
|
256
259
|
const projectInstructionContext = formatProjectInstructions(projectInstructions);
|
|
257
260
|
return {
|
|
@@ -272,6 +275,7 @@ async function createInitialState(config, sessionId) {
|
|
|
272
275
|
truncated: projectInstructions.truncated,
|
|
273
276
|
loadedAt: now,
|
|
274
277
|
},
|
|
278
|
+
selectedSkills: selectedSkills.map((skill) => skill.id),
|
|
275
279
|
},
|
|
276
280
|
chat: [
|
|
277
281
|
{
|
|
@@ -318,6 +322,7 @@ async function createInitialState(config, sessionId) {
|
|
|
318
322
|
? `Parallel DeepSeek scouts may run before complex execution. Scout count: ${config.parallelScoutCount}.`
|
|
319
323
|
: "Parallel scouts are disabled.",
|
|
320
324
|
`Task profile: ${taskProfile.label}. ${taskProfile.prompt}`,
|
|
325
|
+
skillContext,
|
|
321
326
|
engineeringGuidance,
|
|
322
327
|
"A frontend canvas/artifacts tunnel exists. Use send_to_canvas when important markdown, diffs, screenshots, images, or workspace files should be highlighted in the UI. It is optional and ordinary final text can still go directly to finish.",
|
|
323
328
|
"For visual-output requests such as draw, plot, graph, chart, diagram, figure, image, or visualization, proactively publish a canvas artifact even when the user does not mention canvas. If workspace file tools are enabled, prefer creating a small SVG or markdown artifact and call send_to_canvas with selected=true.",
|
|
@@ -360,6 +365,7 @@ async function createInitialState(config, sessionId) {
|
|
|
360
365
|
config.allowWebSearch ? "Web search tool: enabled." : "Web search tool: disabled.",
|
|
361
366
|
config.allowParallelScouts ? `Parallel scouts: enabled count=${config.parallelScoutCount}.` : "Parallel scouts: disabled.",
|
|
362
367
|
`Task profile: ${taskProfile.label}. ${taskProfile.prompt}`,
|
|
368
|
+
skillContext,
|
|
363
369
|
engineeringGuidance,
|
|
364
370
|
"Canvas/artifacts tunnel: available through send_to_canvas for optional frontend rendering.",
|
|
365
371
|
"Visual-output requests should produce a canvas artifact without requiring the user to ask for canvas explicitly.",
|
|
@@ -451,6 +457,8 @@ async function applyContinuationPrompt(state, config, observers) {
|
|
|
451
457
|
|
|
452
458
|
const taskProfile = getTaskProfile(config.taskProfile);
|
|
453
459
|
const engineeringGuidance = engineeringGuidanceForTask(config.goal, config.taskProfile);
|
|
460
|
+
const selectedSkills = selectSkillsForGoal(config.goal, { taskProfile: config.taskProfile, limit: 6 });
|
|
461
|
+
const skillContext = formatSkillsForPrompt(selectedSkills);
|
|
454
462
|
const projectInstructions = await readProjectInstructions(config.baseDir || config.commandCwd || process.cwd());
|
|
455
463
|
state.meta = state.meta || {};
|
|
456
464
|
state.meta.projectInstructions = {
|
|
@@ -459,6 +467,7 @@ async function applyContinuationPrompt(state, config, observers) {
|
|
|
459
467
|
truncated: projectInstructions.truncated,
|
|
460
468
|
loadedAt: new Date().toISOString(),
|
|
461
469
|
};
|
|
470
|
+
state.meta.selectedSkills = selectedSkills.map((skill) => skill.id);
|
|
462
471
|
ensureChatState(state);
|
|
463
472
|
state.goal = config.goal;
|
|
464
473
|
state.provider = config.provider;
|
|
@@ -485,6 +494,7 @@ async function applyContinuationPrompt(state, config, observers) {
|
|
|
485
494
|
? `Agent wrappers: selected=${normalizeWrapperName(config.preferredWrapper)}; ${wrapperStatusText()}`
|
|
486
495
|
: "",
|
|
487
496
|
`Task profile: ${taskProfile.label}. ${taskProfile.prompt}`,
|
|
497
|
+
skillContext,
|
|
488
498
|
engineeringGuidance,
|
|
489
499
|
formatProjectInstructions(projectInstructions),
|
|
490
500
|
"AGINTI.md is editable project memory. If the user asks to remember a preference or update instructions, patch AGINTI.md rather than hiding that preference in session-only chat.",
|
package/src/capabilities.js
CHANGED
|
@@ -9,6 +9,7 @@ import { listTaskProfiles } from "./task-profiles.js";
|
|
|
9
9
|
import { listAgentWrappers } from "./tool-wrappers.js";
|
|
10
10
|
import { listAuxiliarySkills } from "./auxiliary-tools.js";
|
|
11
11
|
import { readCodebaseMap } from "./codebase-map.js";
|
|
12
|
+
import { listSkills } from "./skill-library.js";
|
|
12
13
|
|
|
13
14
|
const execFileAsync = promisify(execFile);
|
|
14
15
|
|
|
@@ -218,6 +219,13 @@ export async function buildCapabilityReport(projectRoot, packageVersion, config)
|
|
|
218
219
|
label: profile.label,
|
|
219
220
|
tools: profile.tools,
|
|
220
221
|
})),
|
|
222
|
+
skills: listSkills().map((skill) => ({
|
|
223
|
+
id: skill.id,
|
|
224
|
+
label: skill.label,
|
|
225
|
+
description: skill.description,
|
|
226
|
+
triggers: skill.triggers,
|
|
227
|
+
tools: skill.tools,
|
|
228
|
+
})),
|
|
221
229
|
auxiliarySkills: listAuxiliarySkills().map((skill) => ({
|
|
222
230
|
id: skill.id,
|
|
223
231
|
label: skill.label,
|
package/src/cli.js
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import { listTaskProfiles } from "./task-profiles.js";
|
|
18
18
|
import { recommendedMaxStepsForTask } from "./engineering-guidance.js";
|
|
19
19
|
import { promptAndSaveDeepSeekKey, promptHidden, shouldPromptForDeepSeek } from "./auth-onboarding.js";
|
|
20
|
+
import { listSkills, selectSkillsForGoal } from "./skill-library.js";
|
|
20
21
|
import fs from "node:fs/promises";
|
|
21
22
|
import path from "node:path";
|
|
22
23
|
import { fileURLToPath } from "node:url";
|
|
@@ -64,6 +65,7 @@ export function parseArgs(argv) {
|
|
|
64
65
|
port: "",
|
|
65
66
|
host: "",
|
|
66
67
|
listProfiles: false,
|
|
68
|
+
listSkills: false,
|
|
67
69
|
latex: false,
|
|
68
70
|
image: false,
|
|
69
71
|
};
|
|
@@ -242,6 +244,10 @@ export function parseArgs(argv) {
|
|
|
242
244
|
result.listProfiles = true;
|
|
243
245
|
continue;
|
|
244
246
|
}
|
|
247
|
+
if (arg === "--list-skills") {
|
|
248
|
+
result.listSkills = true;
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
245
251
|
if (arg === "--sandbox-status") {
|
|
246
252
|
result.sandboxStatus = true;
|
|
247
253
|
continue;
|
|
@@ -261,7 +267,7 @@ export function parseArgs(argv) {
|
|
|
261
267
|
|
|
262
268
|
function printUsage() {
|
|
263
269
|
console.log(
|
|
264
|
-
'Usage: aginti [chat] OR aginti web [--port 3210] OR aginti login deepseek|openai|grsai OR aginti resume [latest|<session-id>] ["prompt"] OR aginti queue <session-id> "message" OR aginti [--image] [--latex] [--routing smart|fast|complex|manual] [--provider deepseek|openai|mock] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-file-tools|--no-file-tools] [--web-search|--no-web-search] [--parallel-scouts|--no-parallel-scouts --scout-count 1..10] [--allow-auxiliary-tools|--no-auxiliary-tools] [--allow-wrappers --wrapper codex] [--sandbox-status|--sandbox-preflight] "your task"'
|
|
270
|
+
'Usage: aginti [chat] OR aginti web [--port 3210] OR aginti skills [query] OR aginti login deepseek|openai|grsai OR aginti resume [latest|<session-id>] ["prompt"] OR aginti queue <session-id> "message" OR aginti [--image] [--latex] [--routing smart|fast|complex|manual] [--provider deepseek|openai|mock] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-file-tools|--no-file-tools] [--web-search|--no-web-search] [--parallel-scouts|--no-parallel-scouts --scout-count 1..10] [--allow-auxiliary-tools|--no-auxiliary-tools] [--allow-wrappers --wrapper codex] [--list-skills] [--sandbox-status|--sandbox-preflight] "your task"'
|
|
265
271
|
);
|
|
266
272
|
}
|
|
267
273
|
|
|
@@ -320,6 +326,17 @@ function printProfiles() {
|
|
|
320
326
|
}
|
|
321
327
|
}
|
|
322
328
|
|
|
329
|
+
function printSkills(query = "") {
|
|
330
|
+
const skills = query
|
|
331
|
+
? selectSkillsForGoal(query, { taskProfile: "auto", limit: 40, includeBody: false })
|
|
332
|
+
: listSkills({ includeBody: false });
|
|
333
|
+
for (const skill of skills) {
|
|
334
|
+
const triggers = skill.triggers?.length ? ` triggers=${skill.triggers.join(",")}` : "";
|
|
335
|
+
const tools = skill.tools?.length ? ` tools=${skill.tools.join(",")}` : "";
|
|
336
|
+
console.log(`${skill.id}: ${skill.label} - ${skill.description}${triggers}${tools}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
323
340
|
function printInitResult(result) {
|
|
324
341
|
console.log(`AgInTiFlow project initialized: ${result.projectRoot}`);
|
|
325
342
|
console.log(`instructions=${result.instructionsPath}`);
|
|
@@ -534,6 +551,11 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
534
551
|
return;
|
|
535
552
|
}
|
|
536
553
|
|
|
554
|
+
if (argv[0] === "skills" || argv[0] === "skill") {
|
|
555
|
+
printSkills(argv.slice(1).join(" ").trim());
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
|
|
537
559
|
if (argv[0] === "queue") {
|
|
538
560
|
await handleQueueCommand(argv.slice(1));
|
|
539
561
|
return;
|
|
@@ -592,6 +614,11 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
592
614
|
return;
|
|
593
615
|
}
|
|
594
616
|
|
|
617
|
+
if (args.listSkills) {
|
|
618
|
+
printSkills(args.goal);
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
|
|
595
622
|
if (args.sandboxStatus || args.sandboxPreflight) {
|
|
596
623
|
const config = loadConfig({ ...args, goal: args.goal || "sandbox preflight" }, { packageDir });
|
|
597
624
|
const result = args.sandboxPreflight
|
package/src/interactive-cli.js
CHANGED
|
@@ -9,6 +9,7 @@ import { defaultMaxStepsForProfile, normalizeTaskProfile } from "./task-profiles
|
|
|
9
9
|
import { recommendedMaxStepsForTask } from "./engineering-guidance.js";
|
|
10
10
|
import { promptAndSaveDeepSeekKey, promptHidden, shouldPromptForDeepSeek } from "./auth-onboarding.js";
|
|
11
11
|
import { SessionStore } from "./session-store.js";
|
|
12
|
+
import { listSkills, selectSkillsForGoal } from "./skill-library.js";
|
|
12
13
|
|
|
13
14
|
const useColor = Boolean(input.isTTY && output.isTTY && process.env.AGINTIFLOW_NO_COLOR !== "1");
|
|
14
15
|
const ansi = {
|
|
@@ -44,6 +45,8 @@ const SLASH_COMMANDS = [
|
|
|
44
45
|
"/new",
|
|
45
46
|
"/resume",
|
|
46
47
|
"/sessions",
|
|
48
|
+
"/skills",
|
|
49
|
+
"/skill",
|
|
47
50
|
"/profile",
|
|
48
51
|
"/web-search",
|
|
49
52
|
"/scouts",
|
|
@@ -480,6 +483,7 @@ function printHelp() {
|
|
|
480
483
|
" /new Start a fresh session on the next message.",
|
|
481
484
|
" /resume <session-id> Continue a saved session.",
|
|
482
485
|
" /sessions List recent sessions in this project.",
|
|
486
|
+
" /skills [query] List Markdown skills selected for a topic.",
|
|
483
487
|
" /profile <name> Set task profile, e.g. code, website, latex, maintenance.",
|
|
484
488
|
" /web-search on|off Enable or disable the web_search tool.",
|
|
485
489
|
" /scouts on|off|<1-10> Enable parallel DeepSeek scouts and set scout count.",
|
|
@@ -1589,6 +1593,30 @@ async function handleCommand(line, state, packageDir) {
|
|
|
1589
1593
|
}
|
|
1590
1594
|
return true;
|
|
1591
1595
|
}
|
|
1596
|
+
if (command === "skills" || command === "skill") {
|
|
1597
|
+
const skills = value
|
|
1598
|
+
? selectSkillsForGoal(value, { taskProfile: state.taskProfile, limit: 12, includeBody: false })
|
|
1599
|
+
: listSkills({ includeBody: false });
|
|
1600
|
+
if (skills.length === 0) {
|
|
1601
|
+
printAgentMessage("No matching skills found.");
|
|
1602
|
+
return true;
|
|
1603
|
+
}
|
|
1604
|
+
printAgentMessage(
|
|
1605
|
+
skills
|
|
1606
|
+
.map((skill) =>
|
|
1607
|
+
[
|
|
1608
|
+
`${skill.id}: ${skill.label}`,
|
|
1609
|
+
` ${skill.description}`,
|
|
1610
|
+
skill.triggers?.length ? ` triggers: ${skill.triggers.join(", ")}` : "",
|
|
1611
|
+
skill.tools?.length ? ` tools: ${skill.tools.join(", ")}` : "",
|
|
1612
|
+
]
|
|
1613
|
+
.filter(Boolean)
|
|
1614
|
+
.join("\n")
|
|
1615
|
+
)
|
|
1616
|
+
.join("\n\n")
|
|
1617
|
+
);
|
|
1618
|
+
return true;
|
|
1619
|
+
}
|
|
1592
1620
|
if (command === "profile") {
|
|
1593
1621
|
state.taskProfile = normalizeTaskProfile(value || "auto");
|
|
1594
1622
|
state.maxSteps = Math.max(state.maxSteps, defaultMaxStepsForProfile(state.taskProfile));
|
package/src/model-client.js
CHANGED
|
@@ -3,6 +3,7 @@ import { normalizeWrapperName, wrapperStatusText } from "./tool-wrappers.js";
|
|
|
3
3
|
import { getTaskProfile } from "./task-profiles.js";
|
|
4
4
|
import { listAuxiliarySkills } from "./auxiliary-tools.js";
|
|
5
5
|
import { engineeringGuidanceForTask } from "./engineering-guidance.js";
|
|
6
|
+
import { formatSkillsForPrompt, selectSkillsForGoal } from "./skill-library.js";
|
|
6
7
|
|
|
7
8
|
export function createClient(config) {
|
|
8
9
|
if (config.provider === "mock") {
|
|
@@ -193,6 +194,8 @@ function mockChatResponse(content, toolCalls = []) {
|
|
|
193
194
|
export async function createPlan(client, config, state) {
|
|
194
195
|
const taskProfile = getTaskProfile(config.taskProfile);
|
|
195
196
|
const engineeringGuidance = engineeringGuidanceForTask(state.goal, config.taskProfile);
|
|
197
|
+
const selectedSkills = selectSkillsForGoal(state.goal, { taskProfile: config.taskProfile, limit: 5 });
|
|
198
|
+
const skillContext = formatSkillsForPrompt(selectedSkills);
|
|
196
199
|
const projectInstructions = state.meta?.projectInstructions;
|
|
197
200
|
if (client.mock) {
|
|
198
201
|
return [
|
|
@@ -242,6 +245,7 @@ export async function createPlan(client, config, state) {
|
|
|
242
245
|
? `Parallel scout notes may be injected before execution for complex tasks. Scout count: ${config.parallelScoutCount}.`
|
|
243
246
|
: "Parallel scouts are disabled.",
|
|
244
247
|
`Task profile: ${taskProfile.label}. ${taskProfile.prompt}`,
|
|
248
|
+
skillContext,
|
|
245
249
|
engineeringGuidance,
|
|
246
250
|
"A canvas/artifacts tunnel is available through send_to_canvas. Use it when an output should be highlighted visually, such as screenshots, image files, important markdown, diffs, or generated artifact paths. It is optional for ordinary text answers.",
|
|
247
251
|
"Work like a practical coding agent: inspect when useful, edit with file tools, run safe checks when they add confidence, and keep outputs inside the workspace.",
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const DEFAULT_SKILLS_DIR = path.resolve(__dirname, "..", "skills");
|
|
7
|
+
const DEFAULT_PROMPT_CHARS = 5200;
|
|
8
|
+
|
|
9
|
+
function parseScalar(value = "") {
|
|
10
|
+
const trimmed = String(value || "").trim();
|
|
11
|
+
if (
|
|
12
|
+
(trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
13
|
+
(trimmed.startsWith("'") && trimmed.endsWith("'"))
|
|
14
|
+
) {
|
|
15
|
+
return trimmed.slice(1, -1);
|
|
16
|
+
}
|
|
17
|
+
return trimmed;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function parseFrontmatter(text, filePath) {
|
|
21
|
+
const match = String(text || "").match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
22
|
+
if (!match) throw new Error(`${filePath}: missing YAML frontmatter`);
|
|
23
|
+
const meta = {};
|
|
24
|
+
const lines = match[1].split(/\r?\n/);
|
|
25
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
26
|
+
const line = lines[index];
|
|
27
|
+
if (!line.trim() || line.trim().startsWith("#")) continue;
|
|
28
|
+
const scalar = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
29
|
+
if (!scalar) throw new Error(`${filePath}: invalid YAML line: ${line}`);
|
|
30
|
+
const key = scalar[1];
|
|
31
|
+
const value = scalar[2];
|
|
32
|
+
if (value.trim()) {
|
|
33
|
+
meta[key] = parseScalar(value);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const items = [];
|
|
37
|
+
while (index + 1 < lines.length && /^\s+-\s+/.test(lines[index + 1])) {
|
|
38
|
+
index += 1;
|
|
39
|
+
items.push(parseScalar(lines[index].replace(/^\s+-\s+/, "")));
|
|
40
|
+
}
|
|
41
|
+
meta[key] = items;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
meta,
|
|
45
|
+
body: String(text || "").slice(match[0].length).trim(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizeList(value) {
|
|
50
|
+
if (Array.isArray(value)) return value.map((item) => String(item || "").trim()).filter(Boolean);
|
|
51
|
+
if (!value) return [];
|
|
52
|
+
return String(value)
|
|
53
|
+
.split(",")
|
|
54
|
+
.map((item) => item.trim())
|
|
55
|
+
.filter(Boolean);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function loadSkillFile(filePath) {
|
|
59
|
+
const text = fs.readFileSync(filePath, "utf8");
|
|
60
|
+
const { meta, body } = parseFrontmatter(text, filePath);
|
|
61
|
+
for (const field of ["id", "label", "description"]) {
|
|
62
|
+
if (typeof meta[field] !== "string" || !meta[field].trim()) {
|
|
63
|
+
throw new Error(`${filePath}: ${field} must be a non-empty string`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
id: meta.id.trim(),
|
|
68
|
+
label: meta.label.trim(),
|
|
69
|
+
description: meta.description.trim(),
|
|
70
|
+
triggers: normalizeList(meta.triggers),
|
|
71
|
+
tools: normalizeList(meta.tools),
|
|
72
|
+
body,
|
|
73
|
+
path: filePath,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function listSkills({ includeBody = false, skillsDir = DEFAULT_SKILLS_DIR } = {}) {
|
|
78
|
+
let dirEntries = [];
|
|
79
|
+
try {
|
|
80
|
+
dirEntries = fs.readdirSync(skillsDir, { withFileTypes: true });
|
|
81
|
+
} catch {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
const skills = [];
|
|
85
|
+
for (const entry of dirEntries) {
|
|
86
|
+
if (!entry.isDirectory()) continue;
|
|
87
|
+
const skillPath = path.join(skillsDir, entry.name, "SKILL.md");
|
|
88
|
+
try {
|
|
89
|
+
const skill = loadSkillFile(skillPath);
|
|
90
|
+
if (!includeBody) delete skill.body;
|
|
91
|
+
skills.push(skill);
|
|
92
|
+
} catch {
|
|
93
|
+
// Invalid local skill files are skipped so one bad skill does not break the agent.
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return skills.sort((a, b) => a.id.localeCompare(b.id));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function scoreSkill(skill, text, taskProfile) {
|
|
100
|
+
let score = 0;
|
|
101
|
+
if (skill.id === taskProfile) score += 10;
|
|
102
|
+
if (skill.triggers.includes(taskProfile)) score += 6;
|
|
103
|
+
for (const trigger of skill.triggers) {
|
|
104
|
+
const needle = trigger.toLowerCase();
|
|
105
|
+
if (needle && text.includes(needle)) score += Math.max(2, Math.min(6, Math.ceil(needle.length / 6)));
|
|
106
|
+
}
|
|
107
|
+
for (const token of skill.description.toLowerCase().split(/[^a-z0-9+#.-]+/).filter((item) => item.length > 3)) {
|
|
108
|
+
if (text.includes(token)) score += 0.25;
|
|
109
|
+
}
|
|
110
|
+
return score;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function selectSkillsForGoal(goal = "", { taskProfile = "auto", limit = 6, includeBody = true } = {}) {
|
|
114
|
+
const text = `${goal} ${taskProfile}`.toLowerCase();
|
|
115
|
+
const skills = listSkills({ includeBody });
|
|
116
|
+
const scored = skills
|
|
117
|
+
.map((skill) => ({ skill, score: scoreSkill(skill, text, taskProfile) }))
|
|
118
|
+
.filter((item) => item.score > 0)
|
|
119
|
+
.sort((a, b) => b.score - a.score || a.skill.id.localeCompare(b.skill.id))
|
|
120
|
+
.map((item) => item.skill);
|
|
121
|
+
return scored.slice(0, Math.max(1, limit));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function compactBody(body = "", limit = 620) {
|
|
125
|
+
const text = String(body || "")
|
|
126
|
+
.replace(/^# .+$/gm, "")
|
|
127
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
128
|
+
.trim();
|
|
129
|
+
if (text.length <= limit) return text;
|
|
130
|
+
return `${text.slice(0, limit - 20).trim()}\n...`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function formatSkillsForPrompt(skills = [], { maxChars = DEFAULT_PROMPT_CHARS } = {}) {
|
|
134
|
+
if (!Array.isArray(skills) || skills.length === 0) return "";
|
|
135
|
+
const chunks = [
|
|
136
|
+
"Selected AgInTiFlow skills. A skill is Markdown guidance for when and how to use tools; it is not itself a tool. Follow relevant skill guidance without becoming constrained by it.",
|
|
137
|
+
];
|
|
138
|
+
for (const skill of skills) {
|
|
139
|
+
chunks.push(
|
|
140
|
+
[
|
|
141
|
+
`## ${skill.id}: ${skill.label}`,
|
|
142
|
+
`Description: ${skill.description}`,
|
|
143
|
+
skill.tools?.length ? `Preferred tools: ${skill.tools.join(", ")}` : "",
|
|
144
|
+
skill.body ? compactBody(skill.body) : "",
|
|
145
|
+
]
|
|
146
|
+
.filter(Boolean)
|
|
147
|
+
.join("\n")
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
const output = chunks.join("\n\n");
|
|
151
|
+
if (output.length <= maxChars) return output;
|
|
152
|
+
return `${output.slice(0, Math.max(maxChars - 80, 1)).trim()}\n... [skills truncated]`;
|
|
153
|
+
}
|
package/web.js
CHANGED
|
@@ -14,6 +14,7 @@ import { summarizeWorkspaceTools, WORKSPACE_TOOL_NAMES } from "./src/workspace-t
|
|
|
14
14
|
import { listTaskProfiles, normalizeTaskProfile } from "./src/task-profiles.js";
|
|
15
15
|
import { loadProjectEnv, projectPaths, providerKeyStatus, setProviderKey } from "./src/project.js";
|
|
16
16
|
import { buildCapabilityReport } from "./src/capabilities.js";
|
|
17
|
+
import { listSkills } from "./src/skill-library.js";
|
|
17
18
|
import {
|
|
18
19
|
buildArtifacts,
|
|
19
20
|
countUnreadArtifacts,
|
|
@@ -574,6 +575,7 @@ app.get("/api/config", async (_req, res) => {
|
|
|
574
575
|
maxSteps: 24,
|
|
575
576
|
},
|
|
576
577
|
taskProfiles: listTaskProfiles(),
|
|
578
|
+
skills: listSkills(),
|
|
577
579
|
routing: {
|
|
578
580
|
modes: ["smart", "fast", "complex", "manual"],
|
|
579
581
|
presets: getModelPresets(),
|