@causyne/agent 1.10.21 → 1.11.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.
Files changed (55) hide show
  1. package/native-skills/media/professional-document-publishing/SKILL.md +44 -0
  2. package/native-skills/media/screenplay-and-shotlist-forge/SKILL.md +42 -0
  3. package/package.json +1 -1
  4. package/src/acp/client.js +235 -0
  5. package/src/acp/client.ts +282 -0
  6. package/src/acp/peerProfiles.js +122 -0
  7. package/src/acp/peerProfiles.ts +128 -0
  8. package/src/acp/protocol.js +54 -0
  9. package/src/acp/protocol.ts +434 -0
  10. package/src/acp/server.js +609 -0
  11. package/src/acp/server.ts +676 -0
  12. package/src/agentLoop.d.ts +43 -0
  13. package/src/agentLoop.js +142 -8
  14. package/src/commands.js +203 -0
  15. package/src/compiledSkills.js +10 -0
  16. package/src/documentEngine/antiSlopAuditor.js +156 -0
  17. package/src/documentEngine/antiSlopAuditor.ts +221 -0
  18. package/src/documentEngine/documentForge.js +443 -0
  19. package/src/documentEngine/documentForge.ts +519 -0
  20. package/src/documentEngine/screenplayFormatter.js +336 -0
  21. package/src/documentEngine/screenplayFormatter.ts +407 -0
  22. package/src/entrypoint.js +13 -0
  23. package/src/execSafe.js +10 -13
  24. package/src/execSafe.ts +25 -0
  25. package/src/modelRouter.js +56 -123
  26. package/src/modelRouter.ts +134 -0
  27. package/src/observer/advisory.js +164 -0
  28. package/src/observer/advisory.ts +171 -0
  29. package/src/observer/coldObserver.js +462 -0
  30. package/src/observer/coldObserver.ts +540 -0
  31. package/src/observer/evidence.js +435 -0
  32. package/src/observer/evidence.ts +499 -0
  33. package/src/perception.js +303 -118
  34. package/src/perception.ts +387 -0
  35. package/src/procSignal.js +15 -16
  36. package/src/procSignal.ts +31 -0
  37. package/src/promptMentions.js +170 -0
  38. package/src/promptMentions.ts +212 -0
  39. package/src/sandbox.js +1 -1
  40. package/src/textDiff.js +117 -109
  41. package/src/textDiff.ts +169 -0
  42. package/src/toolPacks.js +5 -2
  43. package/src/tools/documentForge.js +215 -0
  44. package/src/tools/documentForge.ts +265 -0
  45. package/src/tools/peerAgent.js +102 -0
  46. package/src/tools/peerAgent.ts +116 -0
  47. package/src/tools/registry.js +3 -1
  48. package/src/tui/completion.js +97 -41
  49. package/src/tui/prompt.js +17 -17
  50. package/src/uncertainty.js +93 -205
  51. package/src/uncertainty.ts +255 -0
  52. package/src/utils/paths.js +15 -18
  53. package/src/utils/paths.ts +36 -0
  54. package/src/vfeTracker.js +122 -181
  55. package/src/vfeTracker.ts +242 -0
package/src/perception.js CHANGED
@@ -1,124 +1,309 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
3
- import os from 'os';
4
-
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ const SKIP_DIRECTORIES = new Set([
5
+ 'node_modules', '.git', 'dist', 'build', 'out', 'target',
6
+ '.next', '.nuxt', '.venv', 'venv', '__pycache__', '.gradle',
7
+ '.idea', '.cache', 'coverage', 'vendor', 'Pods', '.causyne'
8
+ ]);
5
9
  export class WorkspacePerception {
6
- async buildContext(workspacePath) {
7
- const context = {};
8
- const resolvedPath = workspacePath.replace(/^~\//, os.homedir() + '/');
9
-
10
- // 1. Directory structure (tree, max depth 2 to keep context size manageable)
11
- context.structure = await this.getDirectoryTree(resolvedPath, 2);
12
-
13
- // 2. Framework detection
14
- context.framework = await this.detectFramework(resolvedPath);
15
-
16
- // 2b. Design system detection (Anti-Slop: tells the brain what's already installed)
17
- context.designSystem = await this.detectDesignContext(resolvedPath);
18
-
19
- // 3. Package/dependency context
20
- try {
21
- const pkgPath = path.join(resolvedPath, 'package.json');
22
- const pkgStats = await fs.stat(pkgPath);
23
- if (pkgStats.isFile()) {
24
- const pkgData = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
25
- context.packageJson = {
26
- name: pkgData.name,
27
- dependencies: pkgData.dependencies || {},
28
- devDependencies: pkgData.devDependencies || {},
29
- scripts: pkgData.scripts || {}
10
+ async buildContext(workspacePath) {
11
+ const resolvedPath = workspacePath.replace(/^~\//, os.homedir() + '/');
12
+ const context = {
13
+ cwd: resolvedPath,
14
+ projectName: path.basename(resolvedPath)
30
15
  };
31
- }
32
- } catch(e) {}
33
-
34
- // 4. README (first 1000 chars)
35
- try {
36
- const readmePath = path.join(resolvedPath, 'README.md');
37
- const readmeContent = await fs.readFile(readmePath, 'utf8');
38
- context.readme = readmeContent.slice(0, 1000) + (readmeContent.length > 1000 ? '...' : '');
39
- } catch(e) {}
40
-
41
- // 5. OS & environment
42
- context.os = { platform: os.platform(), arch: os.arch() };
43
-
44
- return context;
45
- }
46
-
47
- async getDirectoryTree(dir, maxDepth, currentDepth = 0) {
48
- if (currentDepth > maxDepth) return '... (max depth reached)';
49
-
50
- try {
51
- const items = await fs.readdir(dir, { withFileTypes: true });
52
- const tree = {};
53
-
54
- let count = 0;
55
- for (const item of items) {
56
- if (item.name.startsWith('.') || item.name === 'node_modules') continue;
57
-
58
- if (count >= 30) {
59
- tree['...'] = `(${items.length - count} more items hidden)`;
60
- break;
61
- }
62
-
63
- count++;
64
- if (item.isDirectory()) {
65
- tree[item.name] = await this.getDirectoryTree(path.join(dir, item.name), maxDepth, currentDepth + 1);
66
- } else {
67
- tree[item.name] = 'file';
68
- }
69
- }
70
- return tree;
71
- } catch (e) {
72
- return `Error: ${e.message}`;
16
+ // 1. Directory structure (tree, max depth 2 to keep context size manageable)
17
+ context.structure = await this.getDirectoryTree(resolvedPath, 2);
18
+ // 2. Framework detection
19
+ const fw = await this.detectFramework(resolvedPath);
20
+ if (fw && fw.type !== 'unknown') {
21
+ const fwStr = `${fw.type} (${fw.language})`;
22
+ context.framework = Object.assign(new String(fwStr), {
23
+ type: fw.type,
24
+ language: fw.language,
25
+ toString: () => fwStr
26
+ });
27
+ }
28
+ else {
29
+ context.framework = null;
30
+ }
31
+ // 2b. Design system detection (Anti-Slop: tells the brain what's already installed)
32
+ context.designSystem = await this.detectDesignContext(resolvedPath);
33
+ // 2c. Subprojects detection (find subfolders like 'pixel2d games', 'causyne-agent', etc.)
34
+ context.subprojects = await this.detectSubprojects(resolvedPath);
35
+ // 2d. Key files discovery
36
+ context.keyFiles = await this.detectKeyFiles(resolvedPath);
37
+ // 2e. Package manager detection
38
+ context.packageManager = await this.detectPackageManager(resolvedPath);
39
+ // 2f. Git branch and status
40
+ const gitInfo = await this.detectGitInfo(resolvedPath);
41
+ if (gitInfo.branch)
42
+ context.gitBranch = gitInfo.branch;
43
+ if (gitInfo.status)
44
+ context.gitStatus = gitInfo.status;
45
+ // 3. Package/dependency context
46
+ try {
47
+ const pkgPath = path.join(resolvedPath, 'package.json');
48
+ const pkgStats = await fs.stat(pkgPath);
49
+ if (pkgStats.isFile()) {
50
+ const raw = await fs.readFile(pkgPath, 'utf8');
51
+ const pkgData = JSON.parse(raw);
52
+ context.packageJson = {
53
+ name: pkgData.name,
54
+ dependencies: pkgData.dependencies || {},
55
+ devDependencies: pkgData.devDependencies || {},
56
+ scripts: pkgData.scripts || {}
57
+ };
58
+ }
59
+ }
60
+ catch {
61
+ // ignore missing or invalid package.json
62
+ }
63
+ // 3b. Project type classification
64
+ context.projectType = await this.detectProjectType(resolvedPath, context);
65
+ // 4. README (first 1000 chars)
66
+ try {
67
+ const readmePath = path.join(resolvedPath, 'README.md');
68
+ const readmeContent = await fs.readFile(readmePath, 'utf8');
69
+ context.readme = readmeContent.slice(0, 1000) + (readmeContent.length > 1000 ? '...' : '');
70
+ }
71
+ catch {
72
+ // ignore
73
+ }
74
+ // 5. OS & environment
75
+ const osStr = `${os.platform()} (${os.arch()})`;
76
+ context.os = Object.assign(new String(osStr), {
77
+ platform: os.platform(),
78
+ arch: os.arch(),
79
+ toString: () => osStr
80
+ });
81
+ return context;
82
+ }
83
+ async getDirectoryTree(dir, maxDepth, currentDepth = 0) {
84
+ if (currentDepth > maxDepth)
85
+ return '... (max depth reached)';
86
+ try {
87
+ const items = await fs.readdir(dir, { withFileTypes: true });
88
+ const tree = {};
89
+ let count = 0;
90
+ for (const item of items) {
91
+ if (item.name.startsWith('.') || item.name === 'node_modules')
92
+ continue;
93
+ if (count >= 30) {
94
+ tree['...'] = `(${items.length - count} more items hidden)`;
95
+ break;
96
+ }
97
+ count++;
98
+ if (item.isDirectory()) {
99
+ tree[item.name] = await this.getDirectoryTree(path.join(dir, item.name), maxDepth, currentDepth + 1);
100
+ }
101
+ else {
102
+ tree[item.name] = 'file';
103
+ }
104
+ }
105
+ return tree;
106
+ }
107
+ catch (e) {
108
+ const msg = e instanceof Error ? e.message : String(e);
109
+ return `Error: ${msg}`;
110
+ }
111
+ }
112
+ async detectFramework(dir) {
113
+ const detectors = [
114
+ { file: 'next.config.js', type: 'nextjs', lang: 'javascript' },
115
+ { file: 'next.config.ts', type: 'nextjs', lang: 'typescript' },
116
+ { file: 'next.config.mjs', type: 'nextjs', lang: 'javascript' },
117
+ { file: 'vite.config.ts', type: 'vite', lang: 'typescript' },
118
+ { file: 'vite.config.js', type: 'vite', lang: 'javascript' },
119
+ { file: 'nuxt.config.ts', type: 'nuxt', lang: 'typescript' },
120
+ { file: 'astro.config.mjs', type: 'astro', lang: 'javascript' },
121
+ { file: 'svelte.config.js', type: 'svelte', lang: 'javascript' },
122
+ { file: 'tsconfig.json', type: 'node', lang: 'typescript' },
123
+ { file: 'package.json', type: 'node', lang: 'javascript' },
124
+ { file: 'manage.py', type: 'django', lang: 'python' },
125
+ { file: 'pyproject.toml', type: 'python', lang: 'python' },
126
+ { file: 'requirements.txt', type: 'python', lang: 'python' },
127
+ { file: 'Cargo.toml', type: 'rust', lang: 'rust' },
128
+ { file: 'go.mod', type: 'go', lang: 'go' },
129
+ { file: 'project.godot', type: 'godot', lang: 'gdscript' },
130
+ { file: 'build.gradle.kts', type: 'android', lang: 'kotlin' },
131
+ { file: 'docker-compose.yml', type: 'docker', lang: 'various' },
132
+ ];
133
+ for (const d of detectors) {
134
+ try {
135
+ await fs.access(path.join(dir, d.file));
136
+ return { type: d.type, language: d.lang };
137
+ }
138
+ catch {
139
+ // ignore
140
+ }
141
+ }
142
+ return { type: 'unknown', language: 'unknown' };
143
+ }
144
+ async detectDesignContext(dir) {
145
+ const designDetectors = [
146
+ { dep: 'tailwindcss', system: 'Tailwind CSS' },
147
+ { dep: '@astryxdesign/core', system: 'Astryx Design System' },
148
+ { dep: '@mui/material', system: 'Material-UI (MUI)' },
149
+ { dep: '@chakra-ui/react', system: 'Chakra UI' },
150
+ { dep: 'bootstrap', system: 'Bootstrap' },
151
+ { dep: 'styled-components', system: 'Styled Components' },
152
+ { dep: '@emotion/react', system: 'Emotion CSS-in-JS' },
153
+ ];
154
+ try {
155
+ const pkgPath = path.join(dir, 'package.json');
156
+ const raw = await fs.readFile(pkgPath, 'utf8');
157
+ const pkgData = JSON.parse(raw);
158
+ const allDeps = { ...pkgData.dependencies, ...pkgData.devDependencies };
159
+ const detected = designDetectors
160
+ .filter(d => Boolean(allDeps[d.dep]))
161
+ .map(d => d.system);
162
+ return detected.length > 0 ? detected : null;
163
+ }
164
+ catch {
165
+ return null;
166
+ }
167
+ }
168
+ async detectSubprojects(dir) {
169
+ const subprojects = [];
170
+ try {
171
+ const entries = await fs.readdir(dir, { withFileTypes: true });
172
+ for (const entry of entries) {
173
+ if (!entry.isDirectory())
174
+ continue;
175
+ if (entry.name.startsWith('.') || SKIP_DIRECTORIES.has(entry.name))
176
+ continue;
177
+ const subDir = path.join(dir, entry.name);
178
+ const markers = [
179
+ 'package.json', 'index.html', 'project.godot', 'main.py',
180
+ 'Cargo.toml', 'requirements.txt', 'go.mod', 'Makefile', 'CMakeLists.txt'
181
+ ];
182
+ for (const marker of markers) {
183
+ try {
184
+ await fs.access(path.join(subDir, marker));
185
+ subprojects.push(entry.name);
186
+ break;
187
+ }
188
+ catch {
189
+ // ignore
190
+ }
191
+ }
192
+ }
193
+ }
194
+ catch {
195
+ // ignore
196
+ }
197
+ return subprojects;
198
+ }
199
+ async detectKeyFiles(dir) {
200
+ const candidateFiles = [
201
+ 'package.json', 'tsconfig.json', 'index.html', 'main.js',
202
+ 'src/index.js', 'src/index.ts', 'src/main.js', 'src/main.ts',
203
+ 'src/App.tsx', 'src/App.jsx', 'README.md', 'project.godot',
204
+ 'Makefile', 'docker-compose.yml', 'Dockerfile'
205
+ ];
206
+ const found = [];
207
+ for (const rel of candidateFiles) {
208
+ try {
209
+ await fs.access(path.join(dir, rel));
210
+ found.push(rel);
211
+ }
212
+ catch {
213
+ // ignore
214
+ }
215
+ }
216
+ return found;
217
+ }
218
+ async detectPackageManager(dir) {
219
+ const managers = [
220
+ { file: 'pnpm-lock.yaml', name: 'pnpm' },
221
+ { file: 'bun.lockb', name: 'bun' },
222
+ { file: 'bun.lock', name: 'bun' },
223
+ { file: 'yarn.lock', name: 'yarn' },
224
+ { file: 'package-lock.json', name: 'npm' },
225
+ { file: 'Cargo.lock', name: 'cargo' },
226
+ { file: 'Pipfile.lock', name: 'pipenv' },
227
+ { file: 'poetry.lock', name: 'poetry' }
228
+ ];
229
+ for (const m of managers) {
230
+ try {
231
+ await fs.access(path.join(dir, m.file));
232
+ return m.name;
233
+ }
234
+ catch {
235
+ // ignore
236
+ }
237
+ }
238
+ try {
239
+ await fs.access(path.join(dir, 'package.json'));
240
+ return 'npm';
241
+ }
242
+ catch {
243
+ // ignore
244
+ }
245
+ return null;
73
246
  }
74
- }
75
-
76
- async detectFramework(dir) {
77
- const detectors = [
78
- { file: 'next.config.js', type: 'nextjs', lang: 'javascript' },
79
- { file: 'next.config.ts', type: 'nextjs', lang: 'typescript' },
80
- { file: 'vite.config.ts', type: 'vite', lang: 'typescript' },
81
- { file: 'tsconfig.json', type: 'node', lang: 'typescript' },
82
- { file: 'package.json', type: 'node', lang: 'javascript' },
83
- { file: 'manage.py', type: 'django', lang: 'python' },
84
- { file: 'requirements.txt', type: 'python', lang: 'python' },
85
- { file: 'build.gradle.kts', type: 'android', lang: 'kotlin' },
86
- { file: 'docker-compose.yml', type: 'docker', lang: 'various' },
87
- ];
88
-
89
- for (const d of detectors) {
90
- try {
91
- await fs.access(path.join(dir, d.file));
92
- return { type: d.type, language: d.lang };
93
- } catch (e) {}
247
+ async detectGitInfo(dir) {
248
+ let current = dir;
249
+ for (let depth = 0; depth < 5; depth++) {
250
+ const gitDir = path.join(current, '.git');
251
+ try {
252
+ const stat = await fs.stat(gitDir);
253
+ if (stat.isDirectory()) {
254
+ const headFile = path.join(gitDir, 'HEAD');
255
+ const headContent = await fs.readFile(headFile, 'utf8');
256
+ const match = headContent.trim().match(/^ref:\s+refs\/heads\/(.+)$/);
257
+ const branch = match ? (match[1] ?? null) : headContent.trim().slice(0, 8);
258
+ return { branch, gitDir };
259
+ }
260
+ }
261
+ catch {
262
+ // ignore
263
+ }
264
+ const parent = path.dirname(current);
265
+ if (parent === current)
266
+ break;
267
+ current = parent;
268
+ }
269
+ return { branch: null, gitDir: null };
94
270
  }
95
-
96
- return { type: 'unknown', language: 'unknown' };
97
- }
98
-
99
- async detectDesignContext(dir) {
100
- const designDetectors = [
101
- { dep: 'tailwindcss', system: 'Tailwind CSS' },
102
- { dep: '@astryxdesign/core', system: 'Astryx Design System' },
103
- { dep: '@mui/material', system: 'Material-UI (MUI)' },
104
- { dep: '@chakra-ui/react', system: 'Chakra UI' },
105
- { dep: 'bootstrap', system: 'Bootstrap' },
106
- { dep: 'styled-components', system: 'Styled Components' },
107
- { dep: '@emotion/react', system: 'Emotion CSS-in-JS' },
108
- ];
109
-
110
- try {
111
- const pkgPath = path.join(dir, 'package.json');
112
- const pkgData = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
113
- const allDeps = { ...pkgData.dependencies, ...pkgData.devDependencies };
114
-
115
- const detected = designDetectors
116
- .filter(d => allDeps[d.dep])
117
- .map(d => d.system);
118
-
119
- return detected.length > 0 ? detected : null;
120
- } catch (e) {
121
- return null;
271
+ async detectProjectType(dir, context) {
272
+ const deps = context.packageJson ? {
273
+ ...context.packageJson.dependencies,
274
+ ...context.packageJson.devDependencies
275
+ } : {};
276
+ // 1. Game Development (2D Canvas, WebGL, Pixi, Phaser, Godot, Kaplay)
277
+ const gameLibs = ['phaser', 'pixi.js', 'kaboom', 'kaplay', 'three', 'howler', 'matter-js', 'planck'];
278
+ if (gameLibs.some(lib => Boolean(deps[lib]))) {
279
+ return '2D / Canvas Game Project';
280
+ }
281
+ try {
282
+ await fs.access(path.join(dir, 'project.godot'));
283
+ return 'Godot Game Project';
284
+ }
285
+ catch {
286
+ // ignore
287
+ }
288
+ // Check directory name or key files for game patterns
289
+ const baseName = path.basename(dir).toLowerCase();
290
+ if (baseName.includes('game') || baseName.includes('pixel') || baseName.includes('arcade')) {
291
+ return '2D Game Project';
292
+ }
293
+ // 2. Web Application
294
+ if (deps['next'] || deps['react'] || deps['vue'] || deps['svelte'] || deps['vite']) {
295
+ return 'Frontend Web Application';
296
+ }
297
+ // 3. Backend / Server / Service
298
+ if (deps['express'] || deps['fastify'] || deps['koa'] || deps['@nestjs/core']) {
299
+ return 'Backend Web Service';
300
+ }
301
+ if (context.framework && context.framework.type === 'android') {
302
+ return 'Android Application';
303
+ }
304
+ if (context.framework && context.framework.type === 'rust') {
305
+ return 'Rust Application / Library';
306
+ }
307
+ return 'Software Application';
122
308
  }
123
- }
124
309
  }