@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.
Files changed (63) hide show
  1. package/README.md +9 -4
  2. package/docs/agent-runtime-pipe.md +5 -4
  3. package/docs/cli-i18n.md +1 -1
  4. package/docs/project-agent-lessons.md +1 -1
  5. package/docs/real-deepseek-capabilities.md +1 -1
  6. package/docs/self-development-supervision.md +1 -2
  7. package/docs/skills-and-tools.md +1 -1
  8. package/docs/supervised-capability-curriculum.md +111 -0
  9. package/package.json +9 -3
  10. package/public/app.js +4 -0
  11. package/references/backend-frontend-artifact-tunnel.md +3 -3
  12. package/references/general-task-capability-taxonomy.md +109 -0
  13. package/references/supervision-student-recursive-training.md +116 -0
  14. package/scripts/smoke-auto-update.js +96 -0
  15. package/scripts/smoke-auxiliary-tools.js +1 -0
  16. package/scripts/smoke-canvas-artifacts.js +67 -0
  17. package/scripts/smoke-capabilities.js +51 -2
  18. package/scripts/smoke-cli-chat.js +2 -0
  19. package/scripts/smoke-coding-tools.js +41 -0
  20. package/scripts/smoke-inbox.js +40 -1
  21. package/scripts/smoke-skills.js +53 -1
  22. package/scripts/smoke-toolchain-docker.js +1 -0
  23. package/scripts/smoke-web-api.js +6 -1
  24. package/skills/android/SKILL.md +2 -1
  25. package/skills/code-review/SKILL.md +24 -0
  26. package/skills/data-analysis/SKILL.md +29 -0
  27. package/skills/database/SKILL.md +27 -0
  28. package/skills/devops-deployment/SKILL.md +29 -0
  29. package/skills/docs-knowledge/SKILL.md +28 -0
  30. package/skills/dotnet-csharp/SKILL.md +24 -0
  31. package/skills/education-tutorial/SKILL.md +26 -0
  32. package/skills/go/SKILL.md +23 -0
  33. package/skills/ios-swift/SKILL.md +26 -0
  34. package/skills/java-jvm/SKILL.md +25 -0
  35. package/skills/php/SKILL.md +24 -0
  36. package/skills/presentation-slides/SKILL.md +26 -0
  37. package/skills/qa-testing/SKILL.md +27 -0
  38. package/skills/ruby/SKILL.md +24 -0
  39. package/skills/rust/SKILL.md +23 -0
  40. package/skills/security-review/SKILL.md +28 -0
  41. package/skills/supervision-student/SKILL.md +67 -0
  42. package/skills/writing-editing/SKILL.md +28 -0
  43. package/src/agent-runner.js +49 -7
  44. package/src/artifact-tunnel.js +75 -4
  45. package/src/auto-update.js +389 -0
  46. package/src/capabilities.js +6 -1
  47. package/src/cli.js +129 -14
  48. package/src/config.js +6 -2
  49. package/src/engineering-guidance.js +12 -2
  50. package/src/interactive-cli.js +42 -5
  51. package/src/model-client.js +2 -2
  52. package/src/model-routing.js +3 -1
  53. package/src/parallel-scouts.js +17 -1
  54. package/src/project.js +200 -20
  55. package/src/session-index.js +135 -0
  56. package/src/session-store.js +67 -3
  57. package/src/skill-library.js +16 -2
  58. package/src/sqlite.js +19 -0
  59. package/src/static-preview-server.js +8 -1
  60. package/src/task-profiles.js +318 -5
  61. package/src/web-db.js +58 -6
  62. package/src/workspace-tools.js +1 -0
  63. 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: ["aginti-md-init", "capabilities-cli", "doctor-capabilities", "maintenance-policy", "trusted-docker-policy", "git-policy", "skills-capability"],
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
  {
@@ -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: ["session-inbox-append", "session-inbox-drain", "session-inbox-asap-priority"],
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
@@ -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 >= 14, "expected built-in skills to load");
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 });
@@ -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 inboxStore = new SessionStore(path.join(runtimeDir, ".sessions"), runStart.sessionId);
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
 
@@ -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. 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.
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
+