@lazyingart/agintiflow 0.20.25 → 0.20.39
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 +9 -4
- package/docs/agent-runtime-pipe.md +5 -4
- package/docs/cli-i18n.md +1 -1
- package/docs/project-agent-lessons.md +1 -1
- package/docs/real-deepseek-capabilities.md +1 -1
- package/docs/self-development-supervision.md +1 -2
- package/docs/skills-and-tools.md +1 -1
- package/docs/supervised-capability-curriculum.md +111 -0
- package/package.json +9 -3
- package/public/app.js +4 -0
- package/references/backend-frontend-artifact-tunnel.md +3 -3
- package/references/general-task-capability-taxonomy.md +109 -0
- package/references/supervision-student-recursive-training.md +116 -0
- package/scripts/smoke-auto-update.js +96 -0
- package/scripts/smoke-auxiliary-tools.js +1 -0
- package/scripts/smoke-canvas-artifacts.js +67 -0
- package/scripts/smoke-capabilities.js +51 -2
- package/scripts/smoke-cli-chat.js +2 -0
- package/scripts/smoke-coding-tools.js +41 -0
- package/scripts/smoke-inbox.js +40 -1
- package/scripts/smoke-skills.js +53 -1
- package/scripts/smoke-toolchain-docker.js +1 -0
- package/scripts/smoke-web-api.js +6 -1
- package/skills/android/SKILL.md +2 -1
- package/skills/code-review/SKILL.md +24 -0
- package/skills/data-analysis/SKILL.md +29 -0
- package/skills/database/SKILL.md +27 -0
- package/skills/devops-deployment/SKILL.md +29 -0
- package/skills/docs-knowledge/SKILL.md +28 -0
- package/skills/dotnet-csharp/SKILL.md +24 -0
- package/skills/education-tutorial/SKILL.md +26 -0
- package/skills/go/SKILL.md +23 -0
- package/skills/ios-swift/SKILL.md +26 -0
- package/skills/java-jvm/SKILL.md +25 -0
- package/skills/php/SKILL.md +24 -0
- package/skills/presentation-slides/SKILL.md +26 -0
- package/skills/qa-testing/SKILL.md +27 -0
- package/skills/ruby/SKILL.md +24 -0
- package/skills/rust/SKILL.md +23 -0
- package/skills/security-review/SKILL.md +28 -0
- package/skills/supervision-student/SKILL.md +67 -0
- package/skills/writing-editing/SKILL.md +28 -0
- package/src/agent-runner.js +49 -7
- package/src/artifact-tunnel.js +75 -4
- package/src/auto-update.js +389 -0
- package/src/capabilities.js +6 -1
- package/src/cli.js +129 -14
- package/src/config.js +6 -2
- package/src/engineering-guidance.js +12 -2
- package/src/interactive-cli.js +42 -5
- package/src/model-client.js +2 -2
- package/src/model-routing.js +3 -1
- package/src/parallel-scouts.js +17 -1
- package/src/project.js +200 -20
- package/src/session-index.js +135 -0
- package/src/session-store.js +67 -3
- package/src/skill-library.js +16 -2
- package/src/sqlite.js +19 -0
- package/src/static-preview-server.js +8 -1
- package/src/task-profiles.js +318 -5
- package/src/web-db.js +58 -6
- package/src/workspace-tools.js +1 -0
- package/web.js +37 -11
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
compareSemver,
|
|
4
|
+
isGlobalNpmInstall,
|
|
5
|
+
isNewerVersion,
|
|
6
|
+
maybeAutoUpdate,
|
|
7
|
+
shouldAutoUpdateCommand,
|
|
8
|
+
} from "../src/auto-update.js";
|
|
9
|
+
import fs from "node:fs/promises";
|
|
10
|
+
import os from "node:os";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
|
|
13
|
+
function assert(condition, message) {
|
|
14
|
+
if (!condition) throw new Error(message);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const scopedGlobalPath = "/usr/local/lib/node_modules/@lazyingart/agintiflow";
|
|
18
|
+
const windowsGlobalPath = "C:\\Users\\tester\\AppData\\Roaming\\npm\\node_modules\\@lazyingart\\agintiflow";
|
|
19
|
+
|
|
20
|
+
assert(compareSemver("0.20.38", "0.20.37") === 1, "patch version compare failed");
|
|
21
|
+
assert(compareSemver("0.21.0", "0.20.99") === 1, "minor version compare failed");
|
|
22
|
+
assert(compareSemver("1.0.0", "0.99.99") === 1, "major version compare failed");
|
|
23
|
+
assert(compareSemver("0.20.37", "0.20.37") === 0, "equal version compare failed");
|
|
24
|
+
assert(compareSemver("0.20.37", "0.20.38") === -1, "older version compare failed");
|
|
25
|
+
assert(isNewerVersion("0.20.38", "0.20.37"), "newer version predicate failed");
|
|
26
|
+
assert(!isNewerVersion("0.20.37", "0.20.37"), "equal version should not update");
|
|
27
|
+
assert(!isNewerVersion("0.20.36", "0.20.37"), "older latest should not update");
|
|
28
|
+
|
|
29
|
+
assert(isGlobalNpmInstall(scopedGlobalPath), "Unix global npm install path not detected");
|
|
30
|
+
assert(isGlobalNpmInstall(windowsGlobalPath), "Windows global npm install path not detected");
|
|
31
|
+
assert(!isGlobalNpmInstall("/home/user/Projects/AgInTiFlow"), "source checkout should not look globally installed");
|
|
32
|
+
|
|
33
|
+
assert(shouldAutoUpdateCommand([]), "plain interactive start should check updates");
|
|
34
|
+
assert(shouldAutoUpdateCommand(["web"]), "web start should check updates");
|
|
35
|
+
assert(shouldAutoUpdateCommand(["resume", "latest"]), "resume should check updates");
|
|
36
|
+
assert(shouldAutoUpdateCommand(["write", "a", "test"]), "one-shot task should check updates in a TTY");
|
|
37
|
+
assert(!shouldAutoUpdateCommand(["doctor"]), "doctor should skip updates");
|
|
38
|
+
assert(!shouldAutoUpdateCommand(["models"]), "models should skip updates");
|
|
39
|
+
assert(!shouldAutoUpdateCommand(["--version"]), "version should skip updates");
|
|
40
|
+
assert(!shouldAutoUpdateCommand(["task", "--no-auto-update"]), "--no-auto-update should skip updates");
|
|
41
|
+
|
|
42
|
+
const skipped = await maybeAutoUpdate({
|
|
43
|
+
argv: [],
|
|
44
|
+
force: true,
|
|
45
|
+
manual: false,
|
|
46
|
+
packageDir: "/home/user/Projects/AgInTiFlow",
|
|
47
|
+
packageName: "@lazyingart/agintiflow",
|
|
48
|
+
packageVersion: "0.20.37",
|
|
49
|
+
restart: false,
|
|
50
|
+
});
|
|
51
|
+
assert(skipped.skipped === "source-checkout", "source checkout update guard failed");
|
|
52
|
+
|
|
53
|
+
const tempHome = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-auto-update-"));
|
|
54
|
+
const previousHome = process.env.AGINTIFLOW_HOME;
|
|
55
|
+
process.env.AGINTIFLOW_HOME = tempHome;
|
|
56
|
+
await fs.mkdir(tempHome, { recursive: true });
|
|
57
|
+
await fs.writeFile(
|
|
58
|
+
path.join(tempHome, "update-check.json"),
|
|
59
|
+
`${JSON.stringify({ checkedAt: Date.now(), latest: "0.20.99" })}\n`,
|
|
60
|
+
"utf8"
|
|
61
|
+
);
|
|
62
|
+
const writes = [];
|
|
63
|
+
const fakeStdout = {
|
|
64
|
+
isTTY: true,
|
|
65
|
+
write(value) {
|
|
66
|
+
writes.push(String(value));
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
const skipVersion = await maybeAutoUpdate({
|
|
70
|
+
argv: [],
|
|
71
|
+
packageDir: scopedGlobalPath,
|
|
72
|
+
packageName: "@lazyingart/agintiflow",
|
|
73
|
+
packageVersion: "0.20.38",
|
|
74
|
+
restart: false,
|
|
75
|
+
stdout: fakeStdout,
|
|
76
|
+
selectUpdateAction: async () => "skip-version",
|
|
77
|
+
});
|
|
78
|
+
assert(skipVersion.skipped === "skip-version", "skip-version selector choice was not honored");
|
|
79
|
+
const cacheAfterSkip = JSON.parse(await fs.readFile(path.join(tempHome, "update-check.json"), "utf8"));
|
|
80
|
+
assert(cacheAfterSkip.skippedVersion === "0.20.99", "skip-version did not persist skipped version");
|
|
81
|
+
const repeatedSkip = await maybeAutoUpdate({
|
|
82
|
+
argv: [],
|
|
83
|
+
packageDir: scopedGlobalPath,
|
|
84
|
+
packageName: "@lazyingart/agintiflow",
|
|
85
|
+
packageVersion: "0.20.38",
|
|
86
|
+
restart: false,
|
|
87
|
+
stdout: fakeStdout,
|
|
88
|
+
selectUpdateAction: async () => {
|
|
89
|
+
throw new Error("selector should not be called after skip-version");
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
assert(repeatedSkip.skipped === "skipped-version", "cached skipped version was not respected");
|
|
93
|
+
if (previousHome === undefined) delete process.env.AGINTIFLOW_HOME;
|
|
94
|
+
else process.env.AGINTIFLOW_HOME = previousHome;
|
|
95
|
+
|
|
96
|
+
console.log("auto-update smoke ok");
|
|
@@ -11,6 +11,7 @@ import { SessionStore } from "../src/session-store.js";
|
|
|
11
11
|
|
|
12
12
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
13
13
|
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-auxiliary-"));
|
|
14
|
+
process.env.AGINTIFLOW_HOME = path.join(tempRoot, ".agintiflow-home");
|
|
14
15
|
const runtimeDir = path.join(tempRoot, "runtime");
|
|
15
16
|
const workspace = path.join(tempRoot, "workspace");
|
|
16
17
|
await fs.mkdir(workspace, { recursive: true });
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { buildArtifacts, normalizeCanvasPayload, persistCanvasPayloadFile, readArtifactContent } from "../src/artifact-tunnel.js";
|
|
5
|
+
import { SessionStore } from "../src/session-store.js";
|
|
6
|
+
|
|
7
|
+
async function main() {
|
|
8
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), "aginti-canvas-artifacts-"));
|
|
9
|
+
const workspace = path.join(root, "workspace");
|
|
10
|
+
const sessionsDir = path.join(root, "sessions");
|
|
11
|
+
await fs.mkdir(workspace, { recursive: true });
|
|
12
|
+
|
|
13
|
+
const sourcePath = path.join(workspace, "durable-report.md");
|
|
14
|
+
await fs.writeFile(sourcePath, "# Durable report\n\nThis file should survive canvas preview cleanup.\n", "utf8");
|
|
15
|
+
|
|
16
|
+
const config = {
|
|
17
|
+
commandCwd: workspace,
|
|
18
|
+
allowFileTools: true,
|
|
19
|
+
};
|
|
20
|
+
const store = new SessionStore(sessionsDir, "canvas-smoke");
|
|
21
|
+
await store.ensure();
|
|
22
|
+
|
|
23
|
+
const normalized = normalizeCanvasPayload(
|
|
24
|
+
{
|
|
25
|
+
title: "Durable report",
|
|
26
|
+
kind: "markdown",
|
|
27
|
+
path: "durable-report.md",
|
|
28
|
+
selected: true,
|
|
29
|
+
},
|
|
30
|
+
config
|
|
31
|
+
);
|
|
32
|
+
if (!normalized.ok) throw new Error(normalized.reason || "canvas payload normalization failed");
|
|
33
|
+
|
|
34
|
+
const persisted = await persistCanvasPayloadFile(normalized.payload, { config, store });
|
|
35
|
+
if (!persisted.ok) throw new Error(persisted.reason || "canvas artifact persistence failed");
|
|
36
|
+
if (!persisted.payload.artifactPersisted) throw new Error("canvas file was not persisted into session artifacts");
|
|
37
|
+
|
|
38
|
+
await fs.rm(sourcePath);
|
|
39
|
+
|
|
40
|
+
const events = [
|
|
41
|
+
{
|
|
42
|
+
timestamp: new Date().toISOString(),
|
|
43
|
+
type: "canvas.item",
|
|
44
|
+
data: {
|
|
45
|
+
...persisted.payload,
|
|
46
|
+
commandCwd: workspace,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
const { items } = buildArtifacts({ sessionId: store.sessionId, events, store });
|
|
51
|
+
if (items.length !== 1) throw new Error(`expected one artifact, got ${items.length}`);
|
|
52
|
+
|
|
53
|
+
const content = await readArtifactContent(items[0], { store, config });
|
|
54
|
+
if (!content.ok) throw new Error(content.error || "persisted artifact could not be read");
|
|
55
|
+
if (!String(content.text || "").includes("Durable report")) throw new Error("persisted artifact content mismatch");
|
|
56
|
+
|
|
57
|
+
const missing = await persistCanvasPayloadFile({ ...normalized.payload, path: "missing.png" }, { config, store });
|
|
58
|
+
if (missing.ok) throw new Error("missing canvas path should fail persistence");
|
|
59
|
+
|
|
60
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
61
|
+
console.log("smoke-canvas-artifacts ok");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
main().catch((error) => {
|
|
65
|
+
console.error(error);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
});
|
|
@@ -5,16 +5,19 @@ import os from "node:os";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { defaultMaxStepsForProfile, listTaskProfiles } from "../src/task-profiles.js";
|
|
8
9
|
|
|
9
10
|
const execFileAsync = promisify(execFile);
|
|
10
11
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
12
|
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-capabilities-"));
|
|
13
|
+
const agintiflowHome = path.join(tempRoot, ".agintiflow-home");
|
|
14
|
+
process.env.AGINTIFLOW_HOME = agintiflowHome;
|
|
12
15
|
|
|
13
16
|
function assert(condition, message) {
|
|
14
17
|
if (!condition) throw new Error(message);
|
|
15
18
|
}
|
|
16
19
|
|
|
17
|
-
async function runCli(args) {
|
|
20
|
+
async function runCli(args, envOverrides = {}) {
|
|
18
21
|
const result = await execFileAsync(process.execPath, [path.join(repoRoot, "bin/aginti-cli.js"), ...args], {
|
|
19
22
|
cwd: tempRoot,
|
|
20
23
|
timeout: 20000,
|
|
@@ -22,6 +25,8 @@ async function runCli(args) {
|
|
|
22
25
|
env: {
|
|
23
26
|
...process.env,
|
|
24
27
|
AGINTIFLOW_RUNTIME_DIR: "",
|
|
28
|
+
AGINTIFLOW_HOME: agintiflowHome,
|
|
29
|
+
...envOverrides,
|
|
25
30
|
},
|
|
26
31
|
});
|
|
27
32
|
return result.stdout;
|
|
@@ -85,6 +90,21 @@ try {
|
|
|
85
90
|
capabilities.tools?.taskProfiles?.some((profile) => profile.id === "android"),
|
|
86
91
|
"capabilities did not report Android task profile"
|
|
87
92
|
);
|
|
93
|
+
assert(
|
|
94
|
+
capabilities.tools?.taskProfiles?.some((profile) => profile.id === "supervision"),
|
|
95
|
+
"capabilities did not report supervision task profile"
|
|
96
|
+
);
|
|
97
|
+
for (const profileId of ["docs", "data", "qa", "database", "devops", "security", "slides", "education", "java", "ios", "go", "rust", "dotnet", "php", "ruby"]) {
|
|
98
|
+
assert(
|
|
99
|
+
capabilities.tools?.taskProfiles?.some((profile) => profile.id === profileId),
|
|
100
|
+
`capabilities did not report ${profileId} task profile`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
const qaProfile = listTaskProfiles().find((profile) => profile.id === "qa");
|
|
104
|
+
assert(qaProfile, "QA profile is missing");
|
|
105
|
+
assert(defaultMaxStepsForProfile("qa") >= 40, "QA profile step budget is too low for verification and cleanup");
|
|
106
|
+
assert(!/misleading failing test/i.test(qaProfile.prompt), "QA profile still encourages misleading test fixtures");
|
|
107
|
+
assert(/do not stage fake bugs/i.test(qaProfile.prompt), "QA profile does not discourage fake staged failures");
|
|
88
108
|
assert(
|
|
89
109
|
capabilities.trustedDockerPolicy.some((check) => check.command.startsWith("apt-get install") && check.allowed),
|
|
90
110
|
"trusted Docker policy did not allow apt-get install"
|
|
@@ -109,17 +129,46 @@ try {
|
|
|
109
129
|
capabilities.tools?.skills?.some((skill) => skill.id === "android"),
|
|
110
130
|
"capabilities did not report built-in Android skill"
|
|
111
131
|
);
|
|
132
|
+
assert(
|
|
133
|
+
capabilities.tools?.skills?.some((skill) => skill.id === "supervision-student"),
|
|
134
|
+
"capabilities did not report built-in supervision skill"
|
|
135
|
+
);
|
|
136
|
+
for (const skillId of ["data-analysis", "docs-knowledge", "qa-testing", "database", "devops-deployment", "security-review", "presentation-slides", "writing-editing", "java-jvm", "ios-swift", "go", "rust", "dotnet-csharp", "php", "ruby"]) {
|
|
137
|
+
assert(
|
|
138
|
+
capabilities.tools?.skills?.some((skill) => skill.id === skillId),
|
|
139
|
+
`capabilities did not report built-in ${skillId} skill`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
112
142
|
|
|
113
143
|
const doctor = JSON.parse(await runCli(["doctor", "--capabilities", "--json"]));
|
|
114
144
|
assert(doctor.project.root === tempRoot, "doctor --capabilities used the wrong project root");
|
|
115
145
|
assert(doctor.project.instructionsPresent, "doctor --capabilities did not report AGINTI.md");
|
|
146
|
+
const envSandboxRun = await runCli(
|
|
147
|
+
["--provider", "mock", "--routing", "manual", "--model", "mock-agent", "--max-steps", "1", "env sandbox smoke"],
|
|
148
|
+
{
|
|
149
|
+
SANDBOX_MODE: "host",
|
|
150
|
+
PACKAGE_INSTALL_POLICY: "allow",
|
|
151
|
+
USE_DOCKER_SANDBOX: "false",
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
assert(envSandboxRun.includes("Shell: host policy=allow"), "one-shot CLI did not respect host sandbox env defaults");
|
|
155
|
+
assert(!envSandboxRun.includes("Docker workspace:"), "one-shot CLI forced Docker despite host sandbox env defaults");
|
|
116
156
|
|
|
117
157
|
console.log(
|
|
118
158
|
JSON.stringify(
|
|
119
159
|
{
|
|
120
160
|
ok: true,
|
|
121
161
|
projectRoot: tempRoot,
|
|
122
|
-
checks: [
|
|
162
|
+
checks: [
|
|
163
|
+
"aginti-md-init",
|
|
164
|
+
"capabilities-cli",
|
|
165
|
+
"doctor-capabilities",
|
|
166
|
+
"maintenance-policy",
|
|
167
|
+
"trusted-docker-policy",
|
|
168
|
+
"git-policy",
|
|
169
|
+
"skills-capability",
|
|
170
|
+
"env-sandbox-defaults",
|
|
171
|
+
],
|
|
123
172
|
},
|
|
124
173
|
null,
|
|
125
174
|
2
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
|
|
18
18
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
19
19
|
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-cli-chat-"));
|
|
20
|
+
const agintiflowHome = path.join(tempRoot, ".agintiflow-home");
|
|
20
21
|
const binPath = path.join(repoRoot, "bin/aginti-cli.js");
|
|
21
22
|
|
|
22
23
|
function runChat(inputText) {
|
|
@@ -31,6 +32,7 @@ function runCli(args, inputText) {
|
|
|
31
32
|
env: {
|
|
32
33
|
...process.env,
|
|
33
34
|
AGINTIFLOW_RUNTIME_DIR: "",
|
|
35
|
+
AGINTIFLOW_HOME: agintiflowHome,
|
|
34
36
|
AGINTI_LANGUAGE: "en",
|
|
35
37
|
},
|
|
36
38
|
});
|
|
@@ -15,6 +15,7 @@ import { executeWorkspaceTool } from "../src/workspace-tools.js";
|
|
|
15
15
|
|
|
16
16
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
17
17
|
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-coding-tools-"));
|
|
18
|
+
process.env.AGINTIFLOW_HOME = path.join(tempRoot, ".agintiflow-home");
|
|
18
19
|
const runtimeDir = path.join(tempRoot, "runtime");
|
|
19
20
|
const workspace = path.join(tempRoot, "workspace");
|
|
20
21
|
await fs.mkdir(workspace, { recursive: true });
|
|
@@ -97,6 +98,30 @@ try {
|
|
|
97
98
|
taskProfile: "large-codebase",
|
|
98
99
|
});
|
|
99
100
|
assert(/pro/i.test(largeProfileRoute.model), "large-codebase profile did not route to DeepSeek pro");
|
|
101
|
+
const codeProfileRoute = selectModelRoute({
|
|
102
|
+
routingMode: "smart",
|
|
103
|
+
provider: "deepseek",
|
|
104
|
+
goal: "this repo has some bugs and messy parts. can you make it good and leave it clean?",
|
|
105
|
+
taskProfile: "code",
|
|
106
|
+
});
|
|
107
|
+
assert(/pro/i.test(codeProfileRoute.model), "code profile did not route vague bugfix work to DeepSeek pro");
|
|
108
|
+
assert(
|
|
109
|
+
recommendedMaxStepsForTask({
|
|
110
|
+
goal: "this repo has some bugs and messy parts. can you make it good and leave it clean?",
|
|
111
|
+
taskProfile: "code",
|
|
112
|
+
complexityScore: codeProfileRoute.complexityScore,
|
|
113
|
+
}) >= 36,
|
|
114
|
+
"code profile did not get enough steps for inspect-fix-test-cleanup"
|
|
115
|
+
);
|
|
116
|
+
for (const complexProfile of ["qa", "database", "devops", "security"]) {
|
|
117
|
+
const route = selectModelRoute({
|
|
118
|
+
routingMode: "smart",
|
|
119
|
+
provider: "deepseek",
|
|
120
|
+
goal: "do the work",
|
|
121
|
+
taskProfile: complexProfile,
|
|
122
|
+
});
|
|
123
|
+
assert(/pro/i.test(route.model), `${complexProfile} profile did not route to DeepSeek pro`);
|
|
124
|
+
}
|
|
100
125
|
const autoSystemRoute = selectModelRoute({
|
|
101
126
|
routingMode: "smart",
|
|
102
127
|
provider: "deepseek",
|
|
@@ -115,6 +140,22 @@ try {
|
|
|
115
140
|
const guidance = engineeringGuidanceForTask("debug this Python project system bug and fix failing tests", "auto");
|
|
116
141
|
assert(guidance.includes("Python:"), "engineering guidance did not include Python stack advice");
|
|
117
142
|
assert(guidance.includes("System/shell:"), "engineering guidance did not include system stack advice");
|
|
143
|
+
assert(
|
|
144
|
+
guidance.includes("workspace-relative commands"),
|
|
145
|
+
"engineering guidance did not include workspace-relative shell advice"
|
|
146
|
+
);
|
|
147
|
+
assert(
|
|
148
|
+
guidance.includes("obvious duplicates"),
|
|
149
|
+
"engineering guidance did not include output-polish advice"
|
|
150
|
+
);
|
|
151
|
+
assert(
|
|
152
|
+
guidance.includes("clean git status means tracked work is clean"),
|
|
153
|
+
"engineering guidance did not distinguish git-clean from transient artifacts"
|
|
154
|
+
);
|
|
155
|
+
assert(
|
|
156
|
+
guidance.includes("find . -type d -name __pycache__"),
|
|
157
|
+
"engineering guidance did not include recursive Python transient checks"
|
|
158
|
+
);
|
|
118
159
|
assert(
|
|
119
160
|
shouldRunParallelScouts(
|
|
120
161
|
{
|
package/scripts/smoke-inbox.js
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { ensureProjectSessionStorage, listProjectSessions } from "../src/project.js";
|
|
5
6
|
import { SessionStore } from "../src/session-store.js";
|
|
6
7
|
|
|
7
8
|
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-inbox-"));
|
|
9
|
+
process.env.AGINTIFLOW_HOME = path.join(tempRoot, ".agintiflow-home");
|
|
8
10
|
|
|
9
11
|
function assert(condition, message) {
|
|
10
12
|
if (!condition) throw new Error(message);
|
|
@@ -25,11 +27,48 @@ try {
|
|
|
25
27
|
assert(drained[2].content === "second queued message", "second queued message mismatch");
|
|
26
28
|
assert((await store.drainInbox()).length === 0, "inbox was not cleared after drain");
|
|
27
29
|
|
|
30
|
+
const legacyProject = path.join(tempRoot, "legacy-project");
|
|
31
|
+
const legacySession = "legacy-session-smoke";
|
|
32
|
+
const legacyDir = path.join(legacyProject, ".sessions", legacySession);
|
|
33
|
+
await fs.mkdir(legacyDir, { recursive: true });
|
|
34
|
+
await fs.writeFile(
|
|
35
|
+
path.join(legacyDir, "state.json"),
|
|
36
|
+
`${JSON.stringify({
|
|
37
|
+
sessionId: legacySession,
|
|
38
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
39
|
+
updatedAt: "2026-01-01T00:01:00.000Z",
|
|
40
|
+
provider: "mock",
|
|
41
|
+
model: "mock-agent",
|
|
42
|
+
goal: "legacy migration smoke",
|
|
43
|
+
commandCwd: legacyProject,
|
|
44
|
+
chat: [],
|
|
45
|
+
})}\n`,
|
|
46
|
+
"utf8"
|
|
47
|
+
);
|
|
48
|
+
await fs.writeFile(path.join(legacyDir, "events.jsonl"), "", "utf8");
|
|
49
|
+
const paths = await ensureProjectSessionStorage(legacyProject);
|
|
50
|
+
const migrated = await listProjectSessions(legacyProject, 10);
|
|
51
|
+
assert(migrated.some((session) => session.sessionId === legacySession), "legacy session was not discoverable after migration");
|
|
52
|
+
assert(
|
|
53
|
+
await fs.stat(path.join(paths.globalSessionsDir, legacySession, "state.json")).then((stat) => stat.isFile()).catch(() => false),
|
|
54
|
+
"legacy session state was not copied to the global store"
|
|
55
|
+
);
|
|
56
|
+
assert(
|
|
57
|
+
await fs.stat(path.join(paths.sessionsDir, legacySession, "session.json")).then((stat) => stat.isFile()).catch(() => false),
|
|
58
|
+
"project session pointer was not created"
|
|
59
|
+
);
|
|
60
|
+
|
|
28
61
|
console.log(
|
|
29
62
|
JSON.stringify(
|
|
30
63
|
{
|
|
31
64
|
ok: true,
|
|
32
|
-
checks: [
|
|
65
|
+
checks: [
|
|
66
|
+
"session-inbox-append",
|
|
67
|
+
"session-inbox-drain",
|
|
68
|
+
"session-inbox-asap-priority",
|
|
69
|
+
"legacy-session-migration",
|
|
70
|
+
"global-session-store",
|
|
71
|
+
],
|
|
33
72
|
},
|
|
34
73
|
null,
|
|
35
74
|
2
|
package/scripts/smoke-skills.js
CHANGED
|
@@ -18,17 +18,35 @@ function selectedIds(goal, taskProfile = "auto") {
|
|
|
18
18
|
|
|
19
19
|
const skills = listSkills({ includeBody: true });
|
|
20
20
|
const ids = new Set(skills.map((skill) => skill.id));
|
|
21
|
-
assert(skills.length >=
|
|
21
|
+
assert(skills.length >= 27, "expected built-in skills to load");
|
|
22
22
|
for (const required of [
|
|
23
23
|
"aaps",
|
|
24
24
|
"code",
|
|
25
|
+
"code-review",
|
|
26
|
+
"data-analysis",
|
|
27
|
+
"database",
|
|
28
|
+
"dotnet-csharp",
|
|
29
|
+
"devops-deployment",
|
|
30
|
+
"docs-knowledge",
|
|
31
|
+
"education-tutorial",
|
|
32
|
+
"go",
|
|
25
33
|
"github-maintenance",
|
|
26
34
|
"image-generation",
|
|
35
|
+
"ios-swift",
|
|
36
|
+
"java-jvm",
|
|
27
37
|
"latex-manuscript",
|
|
38
|
+
"presentation-slides",
|
|
39
|
+
"qa-testing",
|
|
40
|
+
"ruby",
|
|
41
|
+
"rust",
|
|
42
|
+
"php",
|
|
43
|
+
"security-review",
|
|
28
44
|
"system-maintenance",
|
|
45
|
+
"supervision-student",
|
|
29
46
|
"tmux-session",
|
|
30
47
|
"website-app",
|
|
31
48
|
"word-documents",
|
|
49
|
+
"writing-editing",
|
|
32
50
|
]) {
|
|
33
51
|
assert(ids.has(required), `missing required skill ${required}`);
|
|
34
52
|
}
|
|
@@ -42,6 +60,28 @@ assert(selectedIds("monitor a long running tmux session").includes("tmux-session
|
|
|
42
60
|
assert(selectedIds("create an .aaps example for @lazyingart/aaps").includes("aaps"), "AAPS prompt did not select aaps");
|
|
43
61
|
assert(selectedIds("debug a C++ CMake build").includes("c-cpp"), "C++ prompt did not select c-cpp");
|
|
44
62
|
assert(selectedIds("set up Stan and CmdStanR reproducibly").includes("r-stan"), "Stan prompt did not select r-stan");
|
|
63
|
+
assert(selectedIds("fix this Java Spring Maven JUnit project").includes("java-jvm"), "Java prompt did not select java-jvm");
|
|
64
|
+
assert(selectedIds("build an iOS SwiftUI app and test it in simulator").includes("ios-swift"), "iOS prompt did not select ios-swift");
|
|
65
|
+
assert(selectedIds("repair this Go module and run go test").includes("go"), "Go prompt did not select go");
|
|
66
|
+
assert(selectedIds("fix a Rust cargo workspace and run cargo test").includes("rust"), "Rust prompt did not select rust");
|
|
67
|
+
assert(selectedIds("debug a C# dotnet web API").includes("dotnet-csharp"), ".NET prompt did not select dotnet-csharp");
|
|
68
|
+
assert(selectedIds("fix a PHP Laravel composer project").includes("php"), "PHP prompt did not select php");
|
|
69
|
+
assert(selectedIds("repair a Ruby Rails app with RSpec").includes("ruby"), "Ruby prompt did not select ruby");
|
|
70
|
+
assert(selectedIds("clean a CSV dataset and make plots").includes("data-analysis"), "data prompt did not select data-analysis");
|
|
71
|
+
assert(selectedIds("write README API docs and a tutorial").includes("docs-knowledge"), "docs prompt did not select docs-knowledge");
|
|
72
|
+
assert(selectedIds("fix failing tests and add regression coverage").includes("qa-testing"), "QA prompt did not select qa-testing");
|
|
73
|
+
const qaSkill = skills.find((skill) => skill.id === "qa-testing");
|
|
74
|
+
assert(qaSkill?.body.includes("Do not invent staged bugs"), "QA skill does not guard against fake staged failures");
|
|
75
|
+
assert(selectedIds("write SQL migrations for sqlite schema").includes("database"), "database prompt did not select database");
|
|
76
|
+
assert(selectedIds("debug Docker deployment logs and port config").includes("devops-deployment"), "devops prompt did not select devops-deployment");
|
|
77
|
+
assert(selectedIds("review auth security and secrets handling").includes("security-review"), "security prompt did not select security-review");
|
|
78
|
+
assert(selectedIds("make a PowerPoint pitch deck").includes("presentation-slides"), "slides prompt did not select presentation-slides");
|
|
79
|
+
assert(selectedIds("edit a markdown screenplay final draft").includes("writing-editing"), "writing prompt did not select writing-editing");
|
|
80
|
+
assert(selectedIds("create a lesson with exercises and quiz").includes("education-tutorial"), "education prompt did not select education-tutorial");
|
|
81
|
+
assert(selectedIds("review this PR architecture without editing").includes("code-review"), "review prompt did not select code-review");
|
|
82
|
+
assert(selectedIds("supervise a student agent in tmux and verify its artifacts", "supervision").includes("supervision-student"), "supervision prompt did not select supervision-student");
|
|
83
|
+
assert(selectedIds("supervision").includes("supervision-student"), "single-word supervision prompt did not select supervision-student");
|
|
84
|
+
assert(!selectedIds("supervision").includes("r-stan"), "single-word supervision prompt incorrectly selected r-stan");
|
|
45
85
|
|
|
46
86
|
const prompt = formatSkillsForPrompt(selectSkillsForGoal("write latex manuscript with figures", { taskProfile: "latex", limit: 3 }));
|
|
47
87
|
assert(prompt.includes("A skill is Markdown guidance"), "skill prompt does not explain skill semantics");
|
|
@@ -59,6 +99,18 @@ const cli = await execFileAsync(process.execPath, [path.join(repoRoot, "bin/agin
|
|
|
59
99
|
});
|
|
60
100
|
assert(cli.stdout.includes("website-app"), "aginti skills website did not print website-app");
|
|
61
101
|
|
|
102
|
+
const supervisionCli = await execFileAsync(process.execPath, [path.join(repoRoot, "bin/aginti-cli.js"), "skills", "supervision"], {
|
|
103
|
+
cwd: repoRoot,
|
|
104
|
+
timeout: 10000,
|
|
105
|
+
maxBuffer: 512 * 1024,
|
|
106
|
+
env: {
|
|
107
|
+
...process.env,
|
|
108
|
+
AGINTIFLOW_RUNTIME_DIR: "",
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
assert(supervisionCli.stdout.includes("supervision-student"), "aginti skills supervision did not print supervision-student");
|
|
112
|
+
assert(!supervisionCli.stdout.includes("r-stan:"), "aginti skills supervision incorrectly printed r-stan");
|
|
113
|
+
|
|
62
114
|
console.log(
|
|
63
115
|
JSON.stringify(
|
|
64
116
|
{
|
|
@@ -10,6 +10,7 @@ import { resolveRuntimeConfig } from "../src/config.js";
|
|
|
10
10
|
|
|
11
11
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
12
|
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-toolchain-"));
|
|
13
|
+
process.env.AGINTIFLOW_HOME = path.join(tempRoot, ".agintiflow-home");
|
|
13
14
|
const runtimeDir = path.join(tempRoot, "runtime");
|
|
14
15
|
const workspace = path.join(tempRoot, "workspace");
|
|
15
16
|
await fs.mkdir(workspace, { recursive: true });
|
package/scripts/smoke-web-api.js
CHANGED
|
@@ -5,9 +5,12 @@ import os from "node:os";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { SessionStore } from "../src/session-store.js";
|
|
8
|
+
import { projectPaths, sessionStoreOptions } from "../src/project.js";
|
|
8
9
|
|
|
9
10
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
10
11
|
const runtimeDir = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-api-smoke-"));
|
|
12
|
+
const agintiflowHome = path.join(runtimeDir, ".agintiflow-home");
|
|
13
|
+
process.env.AGINTIFLOW_HOME = agintiflowHome;
|
|
11
14
|
const port = 43000 + Math.floor(Math.random() * 1000);
|
|
12
15
|
const baseUrl = `http://127.0.0.1:${port}`;
|
|
13
16
|
const server = spawn(process.execPath, [path.join(repoRoot, "bin/aginti-cli.js"), "web", "--port", String(port), "--host", "127.0.0.1"], {
|
|
@@ -15,6 +18,7 @@ const server = spawn(process.execPath, [path.join(repoRoot, "bin/aginti-cli.js")
|
|
|
15
18
|
env: {
|
|
16
19
|
...process.env,
|
|
17
20
|
AGINTIFLOW_RUNTIME_DIR: runtimeDir,
|
|
21
|
+
AGINTIFLOW_HOME: agintiflowHome,
|
|
18
22
|
},
|
|
19
23
|
stdio: ["ignore", "pipe", "pipe"],
|
|
20
24
|
});
|
|
@@ -238,7 +242,8 @@ try {
|
|
|
238
242
|
}
|
|
239
243
|
);
|
|
240
244
|
if (!deletedInbox.ok) throw new Error("inbox delete endpoint failed");
|
|
241
|
-
const
|
|
245
|
+
const paths = projectPaths(runtimeDir);
|
|
246
|
+
const inboxStore = new SessionStore(paths.globalSessionsDir, runStart.sessionId, sessionStoreOptions(runtimeDir, runStart.sessionId));
|
|
242
247
|
const remainingInbox = await inboxStore.loadInbox();
|
|
243
248
|
if (remainingInbox.some((item) => item.id === queued.item.id)) throw new Error("inbox delete endpoint left item on disk");
|
|
244
249
|
|
package/skills/android/SKILL.md
CHANGED
|
@@ -26,7 +26,8 @@ tools:
|
|
|
26
26
|
4. Patch source-set-aware files only: manifests, Gradle scripts, Kotlin/Java sources, XML resources, and project docs. Keep generated build outputs out of patches and commits.
|
|
27
27
|
5. Build with the narrowest useful task first, usually `./gradlew :app:assembleDebug` or `./gradlew assembleDebug`. Repair build failures from the actual error text.
|
|
28
28
|
6. Install and launch on an available device/emulator with `adb install` and `adb shell am start`. Verify with `adb shell pidof`, `adb logcat -d`, `adb shell screencap`, or similar evidence when available.
|
|
29
|
-
7.
|
|
29
|
+
7. Save screenshots and APK references in durable workspace paths with descriptive names when the user did not specify paths, for example `artifacts/screenshots/<app>-<timestamp>.png` and `app/build/outputs/apk/debug/app-debug.apk`. Do not rely only on a temporary screenshot that is deleted after canvas preview.
|
|
30
|
+
8. Commit only after build/install/launch verification, with a clear message. Before final summary, run `git status --short`; leave source changes committed and generated/session artifacts ignored or explicitly reported. If no device/emulator exists, finish with a concrete external blocker and the generated APK path.
|
|
30
31
|
|
|
31
32
|
## Missing Tooling
|
|
32
33
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: code-review
|
|
3
|
+
label: Code Review And Architecture Review
|
|
4
|
+
description: Review diffs or codebases for bugs, regressions, design risks, maintainability, missing tests, and architecture issues.
|
|
5
|
+
triggers:
|
|
6
|
+
- review
|
|
7
|
+
- code review
|
|
8
|
+
- architecture review
|
|
9
|
+
- pr review
|
|
10
|
+
- audit code
|
|
11
|
+
- inspect changes
|
|
12
|
+
tools:
|
|
13
|
+
- inspect_project
|
|
14
|
+
- search_files
|
|
15
|
+
- read_file
|
|
16
|
+
- run_command
|
|
17
|
+
- web_search
|
|
18
|
+
---
|
|
19
|
+
# Code Review And Architecture Review
|
|
20
|
+
|
|
21
|
+
Prioritize findings over summary. Inspect changed files, neighboring code, tests, and runtime assumptions. Report concrete risks with file paths, reproduction evidence, and suggested fixes.
|
|
22
|
+
|
|
23
|
+
If no findings are found, say that clearly and name residual risk or missing test coverage. Do not rewrite code during a review unless the user asks for fixes.
|
|
24
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: data-analysis
|
|
3
|
+
label: Data Analysis And ETL
|
|
4
|
+
description: Clean, analyze, transform, validate, visualize, and report on datasets using reproducible scripts and durable artifacts.
|
|
5
|
+
triggers:
|
|
6
|
+
- data
|
|
7
|
+
- csv
|
|
8
|
+
- dataset
|
|
9
|
+
- dataframe
|
|
10
|
+
- pandas
|
|
11
|
+
- analysis
|
|
12
|
+
- etl
|
|
13
|
+
- chart
|
|
14
|
+
- plot
|
|
15
|
+
- visualization
|
|
16
|
+
tools:
|
|
17
|
+
- inspect_project
|
|
18
|
+
- read_file
|
|
19
|
+
- write_file
|
|
20
|
+
- apply_patch
|
|
21
|
+
- run_command
|
|
22
|
+
- send_to_canvas
|
|
23
|
+
---
|
|
24
|
+
# Data Analysis And ETL
|
|
25
|
+
|
|
26
|
+
Preserve raw inputs. Inspect schemas, row counts, missing values, duplicate rows, units, time zones, and suspicious outliers before writing conclusions.
|
|
27
|
+
|
|
28
|
+
Prefer a reproducible script over one-off chat math. Save cleaned data, plots, and reports with descriptive filenames. Rerun the script after edits and verify the output actually changed as intended.
|
|
29
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: database
|
|
3
|
+
label: Database And SQL
|
|
4
|
+
description: Work with SQL, schema design, migrations, seed data, query fixes, and local database-backed apps.
|
|
5
|
+
triggers:
|
|
6
|
+
- database
|
|
7
|
+
- sql
|
|
8
|
+
- sqlite
|
|
9
|
+
- postgres
|
|
10
|
+
- mysql
|
|
11
|
+
- migration
|
|
12
|
+
- schema
|
|
13
|
+
- query
|
|
14
|
+
- seed data
|
|
15
|
+
tools:
|
|
16
|
+
- inspect_project
|
|
17
|
+
- search_files
|
|
18
|
+
- read_file
|
|
19
|
+
- apply_patch
|
|
20
|
+
- run_command
|
|
21
|
+
---
|
|
22
|
+
# Database And SQL
|
|
23
|
+
|
|
24
|
+
Inspect existing migrations, models, schema dumps, seed data, and app access patterns before changing persistence code.
|
|
25
|
+
|
|
26
|
+
Prefer reversible migrations and local fixtures. Never silently drop or rewrite user data. If destructive migration or production credential access would be required, stop and request explicit direction.
|
|
27
|
+
|