@aria_asi/cli 0.2.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/bin/aria.js +168 -0
- package/dist/aria-connector/src/auth-commands.d.ts +28 -0
- package/dist/aria-connector/src/auth-commands.d.ts.map +1 -0
- package/dist/aria-connector/src/auth-commands.js +129 -0
- package/dist/aria-connector/src/auth-commands.js.map +1 -0
- package/dist/aria-connector/src/auth.d.ts +12 -0
- package/dist/aria-connector/src/auth.d.ts.map +1 -0
- package/dist/aria-connector/src/auth.js +31 -0
- package/dist/aria-connector/src/auth.js.map +1 -0
- package/dist/aria-connector/src/auto-mcp.d.ts +23 -0
- package/dist/aria-connector/src/auto-mcp.d.ts.map +1 -0
- package/dist/aria-connector/src/auto-mcp.js +994 -0
- package/dist/aria-connector/src/auto-mcp.js.map +1 -0
- package/dist/aria-connector/src/chat.d.ts +21 -0
- package/dist/aria-connector/src/chat.d.ts.map +1 -0
- package/dist/aria-connector/src/chat.js +332 -0
- package/dist/aria-connector/src/chat.js.map +1 -0
- package/dist/aria-connector/src/codebase-scanner.d.ts +7 -0
- package/dist/aria-connector/src/codebase-scanner.d.ts.map +1 -0
- package/dist/aria-connector/src/codebase-scanner.js +6 -0
- package/dist/aria-connector/src/codebase-scanner.js.map +1 -0
- package/dist/aria-connector/src/cognition-log.d.ts +17 -0
- package/dist/aria-connector/src/cognition-log.d.ts.map +1 -0
- package/dist/aria-connector/src/cognition-log.js +19 -0
- package/dist/aria-connector/src/cognition-log.js.map +1 -0
- package/dist/aria-connector/src/config.d.ts +41 -0
- package/dist/aria-connector/src/config.d.ts.map +1 -0
- package/dist/aria-connector/src/config.js +50 -0
- package/dist/aria-connector/src/config.js.map +1 -0
- package/dist/aria-connector/src/connectors/claude-code.d.ts +4 -0
- package/dist/aria-connector/src/connectors/claude-code.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/claude-code.js +204 -0
- package/dist/aria-connector/src/connectors/claude-code.js.map +1 -0
- package/dist/aria-connector/src/connectors/cursor.d.ts +4 -0
- package/dist/aria-connector/src/connectors/cursor.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/cursor.js +63 -0
- package/dist/aria-connector/src/connectors/cursor.js.map +1 -0
- package/dist/aria-connector/src/connectors/opencode.d.ts +4 -0
- package/dist/aria-connector/src/connectors/opencode.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/opencode.js +102 -0
- package/dist/aria-connector/src/connectors/opencode.js.map +1 -0
- package/dist/aria-connector/src/connectors/shell.d.ts +4 -0
- package/dist/aria-connector/src/connectors/shell.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/shell.js +58 -0
- package/dist/aria-connector/src/connectors/shell.js.map +1 -0
- package/dist/aria-connector/src/garden-client.d.ts +19 -0
- package/dist/aria-connector/src/garden-client.d.ts.map +1 -0
- package/dist/aria-connector/src/garden-client.js +85 -0
- package/dist/aria-connector/src/garden-client.js.map +1 -0
- package/dist/aria-connector/src/garden-control-plane.d.ts +22 -0
- package/dist/aria-connector/src/garden-control-plane.d.ts.map +1 -0
- package/dist/aria-connector/src/garden-control-plane.js +43 -0
- package/dist/aria-connector/src/garden-control-plane.js.map +1 -0
- package/dist/aria-connector/src/harness-client.d.ts +166 -0
- package/dist/aria-connector/src/harness-client.d.ts.map +1 -0
- package/dist/aria-connector/src/harness-client.js +344 -0
- package/dist/aria-connector/src/harness-client.js.map +1 -0
- package/dist/aria-connector/src/hive-client.d.ts +32 -0
- package/dist/aria-connector/src/hive-client.d.ts.map +1 -0
- package/dist/aria-connector/src/hive-client.js +69 -0
- package/dist/aria-connector/src/hive-client.js.map +1 -0
- package/dist/aria-connector/src/index.d.ts +19 -0
- package/dist/aria-connector/src/index.d.ts.map +1 -0
- package/dist/aria-connector/src/index.js +13 -0
- package/dist/aria-connector/src/index.js.map +1 -0
- package/dist/aria-connector/src/install-hooks.d.ts +18 -0
- package/dist/aria-connector/src/install-hooks.d.ts.map +1 -0
- package/dist/aria-connector/src/install-hooks.js +224 -0
- package/dist/aria-connector/src/install-hooks.js.map +1 -0
- package/dist/aria-connector/src/model-context.d.ts +8 -0
- package/dist/aria-connector/src/model-context.d.ts.map +1 -0
- package/dist/aria-connector/src/model-context.js +83 -0
- package/dist/aria-connector/src/model-context.js.map +1 -0
- package/dist/aria-connector/src/persona.d.ts +27 -0
- package/dist/aria-connector/src/persona.d.ts.map +1 -0
- package/dist/aria-connector/src/persona.js +86 -0
- package/dist/aria-connector/src/persona.js.map +1 -0
- package/dist/aria-connector/src/providers/anthropic.d.ts +4 -0
- package/dist/aria-connector/src/providers/anthropic.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/anthropic.js +92 -0
- package/dist/aria-connector/src/providers/anthropic.js.map +1 -0
- package/dist/aria-connector/src/providers/deepseek.d.ts +3 -0
- package/dist/aria-connector/src/providers/deepseek.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/deepseek.js +28 -0
- package/dist/aria-connector/src/providers/deepseek.js.map +1 -0
- package/dist/aria-connector/src/providers/google.d.ts +3 -0
- package/dist/aria-connector/src/providers/google.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/google.js +38 -0
- package/dist/aria-connector/src/providers/google.js.map +1 -0
- package/dist/aria-connector/src/providers/ollama.d.ts +3 -0
- package/dist/aria-connector/src/providers/ollama.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/ollama.js +28 -0
- package/dist/aria-connector/src/providers/ollama.js.map +1 -0
- package/dist/aria-connector/src/providers/openai.d.ts +4 -0
- package/dist/aria-connector/src/providers/openai.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/openai.js +84 -0
- package/dist/aria-connector/src/providers/openai.js.map +1 -0
- package/dist/aria-connector/src/providers/openrouter.d.ts +3 -0
- package/dist/aria-connector/src/providers/openrouter.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/openrouter.js +30 -0
- package/dist/aria-connector/src/providers/openrouter.js.map +1 -0
- package/dist/aria-connector/src/providers/types.d.ts +20 -0
- package/dist/aria-connector/src/providers/types.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/types.js +2 -0
- package/dist/aria-connector/src/providers/types.js.map +1 -0
- package/dist/aria-connector/src/setup-wizard.d.ts +2 -0
- package/dist/aria-connector/src/setup-wizard.d.ts.map +1 -0
- package/dist/aria-connector/src/setup-wizard.js +140 -0
- package/dist/aria-connector/src/setup-wizard.js.map +1 -0
- package/dist/aria-connector/src/types.d.ts +30 -0
- package/dist/aria-connector/src/types.d.ts.map +1 -0
- package/dist/aria-connector/src/types.js +5 -0
- package/dist/aria-connector/src/types.js.map +1 -0
- package/dist/aria-web/src/lib/codebase-scanner.d.ts +127 -0
- package/dist/aria-web/src/lib/codebase-scanner.d.ts.map +1 -0
- package/dist/aria-web/src/lib/codebase-scanner.js +1730 -0
- package/dist/aria-web/src/lib/codebase-scanner.js.map +1 -0
- package/dist/cli-0.2.0.tgz +0 -0
- package/dist/install.sh +13 -0
- package/hooks/aria-harness-via-sdk.mjs +317 -0
- package/hooks/aria-pre-tool-gate.mjs +596 -0
- package/hooks/aria-preprompt-consult.mjs +175 -0
- package/hooks/aria-stop-gate.mjs +222 -0
- package/package.json +47 -0
- package/src/__tests__/auth-commands.test.ts +132 -0
- package/src/auth-commands.ts +175 -0
- package/src/auth.ts +33 -0
- package/src/auto-mcp.ts +1172 -0
- package/src/chat.ts +387 -0
- package/src/codebase-scanner.ts +18 -0
- package/src/cognition-log.ts +30 -0
- package/src/config.ts +94 -0
- package/src/connectors/claude-code.ts +213 -0
- package/src/connectors/cursor.ts +75 -0
- package/src/connectors/opencode.ts +115 -0
- package/src/connectors/shell.ts +72 -0
- package/src/garden-client.ts +98 -0
- package/src/garden-control-plane.ts +108 -0
- package/src/harness-client.ts +454 -0
- package/src/hive-client.ts +104 -0
- package/src/index.ts +26 -0
- package/src/install-hooks.ts +259 -0
- package/src/model-context.ts +88 -0
- package/src/persona.ts +113 -0
- package/src/providers/anthropic.ts +120 -0
- package/src/providers/deepseek.ts +40 -0
- package/src/providers/google.ts +57 -0
- package/src/providers/ollama.ts +43 -0
- package/src/providers/openai.ts +108 -0
- package/src/providers/openrouter.ts +42 -0
- package/src/providers/types.ts +35 -0
- package/src/setup-wizard.ts +177 -0
- package/src/types.ts +32 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, copyFileSync, chmodSync, mkdirSync } from 'fs';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import type { AriaConfig } from '../config.js';
|
|
6
|
+
|
|
7
|
+
// ── Hooks shipped with this package ──────────────────────────────────
|
|
8
|
+
// The connector installs these into ~/.claude/hooks/ so Claude Code's
|
|
9
|
+
// per-turn harness fetch + pre-tool gate fire automatically. This is
|
|
10
|
+
// the difference between "static prompt addendum" (what this connector
|
|
11
|
+
// used to do) and "real runtime control plane" (what Aria's harness
|
|
12
|
+
// actually is).
|
|
13
|
+
//
|
|
14
|
+
// Resolved relative to the compiled connector file: at runtime the
|
|
15
|
+
// .js lives at <pkg>/dist/src/connectors/claude-code.js, hooks are at
|
|
16
|
+
// <pkg>/hooks/ — so up three levels then into hooks/.
|
|
17
|
+
const HOOK_FILES = ['aria-harness-via-sdk.mjs', 'aria-pre-tool-gate.mjs'];
|
|
18
|
+
function packageHooksDir(): string {
|
|
19
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
return path.resolve(here, '..', '..', '..', 'hooks');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Hook wiring for ~/.claude/settings.json. Mirrors what
|
|
24
|
+
// ops/claude-hooks/install.sh writes; this is the customer-facing
|
|
25
|
+
// equivalent that ships with the npm package so anyone running
|
|
26
|
+
// `aria connect claude-code` gets the same behavior as the internal
|
|
27
|
+
// dev install.
|
|
28
|
+
const HOOKS_BLOCK = {
|
|
29
|
+
SessionStart: [{
|
|
30
|
+
hooks: [{
|
|
31
|
+
type: 'command',
|
|
32
|
+
command: 'HOOK_EVENT_NAME=SessionStart node $HOME/.claude/hooks/aria-harness-via-sdk.mjs --mode session',
|
|
33
|
+
timeout: 14,
|
|
34
|
+
statusMessage: 'Fetching Aria harness packet...',
|
|
35
|
+
}],
|
|
36
|
+
}],
|
|
37
|
+
UserPromptSubmit: [{
|
|
38
|
+
hooks: [{
|
|
39
|
+
type: 'command',
|
|
40
|
+
command: 'HOOK_EVENT_NAME=UserPromptSubmit node $HOME/.claude/hooks/aria-harness-via-sdk.mjs --mode turn',
|
|
41
|
+
timeout: 5,
|
|
42
|
+
statusMessage: 'Re-syncing Aria harness...',
|
|
43
|
+
}],
|
|
44
|
+
}],
|
|
45
|
+
PreToolUse: [{
|
|
46
|
+
matcher: 'Bash',
|
|
47
|
+
hooks: [{
|
|
48
|
+
type: 'command',
|
|
49
|
+
command: 'node $HOME/.claude/hooks/aria-pre-tool-gate.mjs',
|
|
50
|
+
timeout: 5,
|
|
51
|
+
}],
|
|
52
|
+
}],
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function installHooks(claudeDir: string, logs: string[]): void {
|
|
56
|
+
const hooksTargetDir = path.join(claudeDir, 'hooks');
|
|
57
|
+
if (!existsSync(hooksTargetDir)) {
|
|
58
|
+
mkdirSync(hooksTargetDir, { recursive: true });
|
|
59
|
+
}
|
|
60
|
+
const sourceDir = packageHooksDir();
|
|
61
|
+
for (const name of HOOK_FILES) {
|
|
62
|
+
const src = path.join(sourceDir, name);
|
|
63
|
+
if (!existsSync(src)) {
|
|
64
|
+
logs.push(`⚠ hook source missing: ${src} (package may be incomplete)`);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const dst = path.join(hooksTargetDir, name);
|
|
68
|
+
// Back up any existing real file before overwriting (never silently
|
|
69
|
+
// clobber a user's customizations).
|
|
70
|
+
if (existsSync(dst) && !isSamePath(src, dst)) {
|
|
71
|
+
const backup = `${dst}.pre-aria-connect.${Date.now()}`;
|
|
72
|
+
try { copyFileSync(dst, backup); logs.push(`Backed up existing ${name} → ${path.basename(backup)}`); } catch {}
|
|
73
|
+
}
|
|
74
|
+
copyFileSync(src, dst);
|
|
75
|
+
try { chmodSync(dst, 0o755); } catch {}
|
|
76
|
+
logs.push(`Installed hook: ${name}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function isSamePath(a: string, b: string): boolean {
|
|
81
|
+
try { return path.resolve(a) === path.resolve(b); } catch { return false; }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function wireHooksBlock(settings: Record<string, unknown>, logs: string[]): void {
|
|
85
|
+
const hooks = (settings.hooks as Record<string, unknown> | undefined) ?? {};
|
|
86
|
+
// Replace each event's block wholesale so re-running connect stays
|
|
87
|
+
// deterministic and doesn't accumulate duplicate entries.
|
|
88
|
+
for (const [event, block] of Object.entries(HOOKS_BLOCK)) {
|
|
89
|
+
hooks[event] = block;
|
|
90
|
+
}
|
|
91
|
+
settings.hooks = hooks;
|
|
92
|
+
logs.push('Wired hooks (SessionStart, UserPromptSubmit, PreToolUse=Bash) into settings.json');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function buildAriaSystemBlock(config: AriaConfig): string {
|
|
96
|
+
const repoList = config.repositories.map((r) => `- ${r.name} (${r.path})`).join('\n');
|
|
97
|
+
const schemaText = Object.entries(config.schemaImages)
|
|
98
|
+
.map(([name, image]) => `[${name}]\n${image}`)
|
|
99
|
+
.join('\n\n');
|
|
100
|
+
|
|
101
|
+
return `<!-- ARIA HARNESS — injected by @aria/connector -->
|
|
102
|
+
You are augmented with Aria's cognitive harness. This provides:
|
|
103
|
+
- Schema image: a live snapshot of the codebase structure, dependencies, and patterns
|
|
104
|
+
- Garden memory: persistent project memory across sessions
|
|
105
|
+
- 8-lens cognition: multi-perspective analysis for every decision
|
|
106
|
+
|
|
107
|
+
[SELF-GATE PROTOCOL]
|
|
108
|
+
Before emitting any claim, verify against these hard constraints:
|
|
109
|
+
1. Truth over deception — always.
|
|
110
|
+
2. No harm supersedes any advantage.
|
|
111
|
+
3. Sacred trust — never betray.
|
|
112
|
+
4. Power obligates service.
|
|
113
|
+
5. Reflection before action.
|
|
114
|
+
|
|
115
|
+
[CONNECTED REPOSITORIES]
|
|
116
|
+
${repoList || '(none linked yet — run `aria repo link <path>`)'}
|
|
117
|
+
|
|
118
|
+
[SCHEMA IMAGES]
|
|
119
|
+
${schemaText || '(no schema images yet — run `aria repo scan`)'}
|
|
120
|
+
|
|
121
|
+
[INSTRUCTIONS]
|
|
122
|
+
- Refer to the schema image for codebase context before answering.
|
|
123
|
+
- Use garden memory to maintain continuity across sessions.
|
|
124
|
+
- Apply the 8-lens analysis for complex decisions.
|
|
125
|
+
- Flag uncertainty; never fabricate code or facts.
|
|
126
|
+
<!-- END ARIA HARNESS -->`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function connectClaudeCode(config: AriaConfig): Promise<string[]> {
|
|
130
|
+
const logs: string[] = [];
|
|
131
|
+
const claudeDir = path.join(homedir(), '.claude');
|
|
132
|
+
|
|
133
|
+
if (!existsSync(claudeDir)) {
|
|
134
|
+
const { mkdirSync } = await import('fs');
|
|
135
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
136
|
+
logs.push(`Created ${claudeDir}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
140
|
+
let settings: Record<string, unknown> = {};
|
|
141
|
+
|
|
142
|
+
if (existsSync(settingsPath)) {
|
|
143
|
+
try {
|
|
144
|
+
settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
145
|
+
} catch {
|
|
146
|
+
logs.push('Existing settings.json was corrupt — starting fresh');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const ariaBlock = buildAriaSystemBlock(config);
|
|
151
|
+
|
|
152
|
+
const existing = typeof settings.systemPromptPrefix === 'string'
|
|
153
|
+
? settings.systemPromptPrefix
|
|
154
|
+
: '';
|
|
155
|
+
|
|
156
|
+
if (existing.includes('ARIA HARNESS')) {
|
|
157
|
+
logs.push('Aria harness prefix already present — skipping prompt injection');
|
|
158
|
+
} else {
|
|
159
|
+
settings.systemPromptPrefix = existing
|
|
160
|
+
? `${ariaBlock}\n\n${existing}`
|
|
161
|
+
: ariaBlock;
|
|
162
|
+
logs.push('Injected Aria harness into Claude Code systemPromptPrefix');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Install hook scripts + wire them into settings.hooks block. This is
|
|
166
|
+
// what makes the connector a real runtime control plane rather than
|
|
167
|
+
// just a system-prompt addendum.
|
|
168
|
+
installHooks(claudeDir, logs);
|
|
169
|
+
wireHooksBlock(settings, logs);
|
|
170
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
171
|
+
|
|
172
|
+
const configJsonPath = path.join(claudeDir, 'config.json');
|
|
173
|
+
if (existsSync(configJsonPath)) {
|
|
174
|
+
try {
|
|
175
|
+
const claudeConfig = JSON.parse(readFileSync(configJsonPath, 'utf-8'));
|
|
176
|
+
if (typeof claudeConfig.additionalDirectories === 'undefined') {
|
|
177
|
+
claudeConfig.additionalDirectories = [];
|
|
178
|
+
}
|
|
179
|
+
const ariaDir = path.join(homedir(), '.aria');
|
|
180
|
+
if (!claudeConfig.additionalDirectories.includes(ariaDir)) {
|
|
181
|
+
claudeConfig.additionalDirectories.push(ariaDir);
|
|
182
|
+
writeFileSync(configJsonPath, JSON.stringify(claudeConfig, null, 2));
|
|
183
|
+
logs.push('Added ~/.aria to Claude Code additional directories');
|
|
184
|
+
}
|
|
185
|
+
} catch {
|
|
186
|
+
// config.json may not be valid JSON or not exist
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return logs;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export async function disconnectClaudeCode(): Promise<string[]> {
|
|
194
|
+
const logs: string[] = [];
|
|
195
|
+
const settingsPath = path.join(homedir(), '.claude', 'settings.json');
|
|
196
|
+
|
|
197
|
+
if (!existsSync(settingsPath)) {
|
|
198
|
+
logs.push('No Claude Code settings found');
|
|
199
|
+
return logs;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
203
|
+
if (typeof settings.systemPromptPrefix === 'string') {
|
|
204
|
+
settings.systemPromptPrefix = settings.systemPromptPrefix.replace(
|
|
205
|
+
/<!-- ARIA HARNESS.*?<!-- END ARIA HARNESS -->/s,
|
|
206
|
+
'',
|
|
207
|
+
).trim();
|
|
208
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
209
|
+
logs.push('Removed Aria harness from Claude Code settings');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return logs;
|
|
213
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import type { AriaConfig } from '../config.js';
|
|
4
|
+
|
|
5
|
+
const CURSOR_RULES_HEADER = `// ARIA HARNESS — injected by @aria/connector
|
|
6
|
+
// Aria provides: schema image awareness + garden memory + 8-lens cognition
|
|
7
|
+
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
function buildCursorRules(config: AriaConfig): string {
|
|
11
|
+
const lines: string[] = [CURSOR_RULES_HEADER];
|
|
12
|
+
|
|
13
|
+
if (config.repositories.length > 0) {
|
|
14
|
+
lines.push('// Connected repositories:');
|
|
15
|
+
for (const repo of config.repositories) {
|
|
16
|
+
lines.push(`// ${repo.name} — ${repo.path}`);
|
|
17
|
+
}
|
|
18
|
+
lines.push('');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
lines.push('// Always reference the schema image for codebase context.');
|
|
22
|
+
lines.push('// Apply the 8-lens analysis for complex architectural decisions.');
|
|
23
|
+
lines.push('// Persist key decisions to garden memory for session continuity.');
|
|
24
|
+
lines.push('');
|
|
25
|
+
lines.push('// Self-gate: verify claims before emitting. Flag uncertainty.');
|
|
26
|
+
|
|
27
|
+
return lines.join('\n');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function connectCursor(config: AriaConfig, repoPath?: string): Promise<string[]> {
|
|
31
|
+
const logs: string[] = [];
|
|
32
|
+
const targetPath = repoPath || process.cwd();
|
|
33
|
+
const rulesPath = path.join(targetPath, '.cursorrules');
|
|
34
|
+
|
|
35
|
+
const ariaBlock = buildCursorRules(config);
|
|
36
|
+
|
|
37
|
+
if (existsSync(rulesPath)) {
|
|
38
|
+
const existing = readFileSync(rulesPath, 'utf-8');
|
|
39
|
+
if (existing.includes('ARIA HARNESS')) {
|
|
40
|
+
logs.push('Aria harness already in .cursorrules — skipping');
|
|
41
|
+
return logs;
|
|
42
|
+
}
|
|
43
|
+
writeFileSync(rulesPath, ariaBlock + '\n' + existing);
|
|
44
|
+
logs.push(`Prepended Aria harness to ${rulesPath}`);
|
|
45
|
+
} else {
|
|
46
|
+
writeFileSync(rulesPath, ariaBlock);
|
|
47
|
+
logs.push(`Created ${rulesPath} with Aria harness`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const cursorRulesDir = path.join(targetPath, '.cursor', 'rules');
|
|
51
|
+
if (existsSync(cursorRulesDir)) {
|
|
52
|
+
const ariaRulePath = path.join(cursorRulesDir, 'aria-harness.md');
|
|
53
|
+
if (!existsSync(ariaRulePath)) {
|
|
54
|
+
writeFileSync(ariaRulePath, buildCursorRules(config));
|
|
55
|
+
logs.push(`Created ${ariaRulePath}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return logs;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function disconnectCursor(repoPath?: string): Promise<string[]> {
|
|
63
|
+
const logs: string[] = [];
|
|
64
|
+
const targetPath = repoPath || process.cwd();
|
|
65
|
+
const rulesPath = path.join(targetPath, '.cursorrules');
|
|
66
|
+
|
|
67
|
+
if (existsSync(rulesPath)) {
|
|
68
|
+
let content = readFileSync(rulesPath, 'utf-8');
|
|
69
|
+
content = content.replace(/\/\/ ARIA HARNESS[\s\S]*?\n\n/, '');
|
|
70
|
+
writeFileSync(rulesPath, content);
|
|
71
|
+
logs.push(`Removed Aria harness from ${rulesPath}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return logs;
|
|
75
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import type { AriaConfig } from '../config.js';
|
|
5
|
+
|
|
6
|
+
export async function connectOpenCode(config: AriaConfig): Promise<string[]> {
|
|
7
|
+
const logs: string[] = [];
|
|
8
|
+
const opencodeDir = path.join(homedir(), '.opencode');
|
|
9
|
+
|
|
10
|
+
if (!existsSync(opencodeDir)) {
|
|
11
|
+
logs.push('No ~/.opencode directory found — OpenCode may not be installed');
|
|
12
|
+
return logs;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const configPath = path.join(opencodeDir, 'config.json');
|
|
16
|
+
if (existsSync(configPath)) {
|
|
17
|
+
try {
|
|
18
|
+
const opencodeConfig = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
19
|
+
let modified = false;
|
|
20
|
+
|
|
21
|
+
if (Array.isArray(opencodeConfig.plugins)) {
|
|
22
|
+
if (!opencodeConfig.plugins.includes('@aria/connector')) {
|
|
23
|
+
opencodeConfig.plugins.push('@aria/connector');
|
|
24
|
+
modified = true;
|
|
25
|
+
logs.push('Added @aria/connector to OpenCode plugins');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!opencodeConfig.agentsMdPath) {
|
|
30
|
+
opencodeConfig.agentsMdPath = path.join(homedir(), '.aria', 'AGENTS.md');
|
|
31
|
+
modified = true;
|
|
32
|
+
logs.push('Set OpenCode AGENTS.md path to ~/.aria/AGENTS.md');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (modified) {
|
|
36
|
+
writeFileSync(configPath, JSON.stringify(opencodeConfig, null, 2));
|
|
37
|
+
} else {
|
|
38
|
+
logs.push('OpenCode already configured for Aria');
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
logs.push('Failed to parse OpenCode config.json');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const ariaDir = path.join(homedir(), '.aria');
|
|
46
|
+
if (!existsSync(ariaDir)) {
|
|
47
|
+
mkdirSync(ariaDir, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const ariaAgentsPath = path.join(ariaDir, 'AGENTS.md');
|
|
51
|
+
const agentsContent = buildOpenCodeAgentsMd(config);
|
|
52
|
+
writeFileSync(ariaAgentsPath, agentsContent);
|
|
53
|
+
logs.push(`Wrote Aria harness AGENTS.md to ${ariaAgentsPath}`);
|
|
54
|
+
|
|
55
|
+
return logs;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function disconnectOpenCode(): Promise<string[]> {
|
|
59
|
+
const logs: string[] = [];
|
|
60
|
+
const configPath = path.join(homedir(), '.opencode', 'config.json');
|
|
61
|
+
|
|
62
|
+
if (existsSync(configPath)) {
|
|
63
|
+
try {
|
|
64
|
+
const opencodeConfig = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
65
|
+
if (Array.isArray(opencodeConfig.plugins)) {
|
|
66
|
+
opencodeConfig.plugins = opencodeConfig.plugins.filter(
|
|
67
|
+
(p: string) => p !== '@aria/connector',
|
|
68
|
+
);
|
|
69
|
+
writeFileSync(configPath, JSON.stringify(opencodeConfig, null, 2));
|
|
70
|
+
logs.push('Removed @aria/connector from OpenCode plugins');
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
logs.push('Failed to update OpenCode config');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return logs;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function buildOpenCodeAgentsMd(config: AriaConfig): string {
|
|
81
|
+
const repoList = config.repositories.map((r) => `- ${r.name} (${r.path})`).join('\n');
|
|
82
|
+
const schemaText = Object.entries(config.schemaImages)
|
|
83
|
+
.map(([name, image]) => `### ${name}\n${image}`)
|
|
84
|
+
.join('\n\n');
|
|
85
|
+
|
|
86
|
+
return `# Aria Harness — AGENTS.md
|
|
87
|
+
|
|
88
|
+
Automatically injected by @aria/connector. This file provides Aria's cognitive harness
|
|
89
|
+
to OpenCode sessions.
|
|
90
|
+
|
|
91
|
+
## Connected Repositories
|
|
92
|
+
${repoList || '(none linked yet)'}
|
|
93
|
+
|
|
94
|
+
## Schema Images
|
|
95
|
+
${schemaText || '(no schema images yet — run \`aria repo scan\`)'}
|
|
96
|
+
|
|
97
|
+
## Self-Gate Protocol
|
|
98
|
+
1. Truth over deception — always.
|
|
99
|
+
2. No harm supersedes any advantage.
|
|
100
|
+
3. Sacred trust — never betray.
|
|
101
|
+
4. Power obligates service.
|
|
102
|
+
5. Reflection before action.
|
|
103
|
+
|
|
104
|
+
## 8-Lens Cognition
|
|
105
|
+
Apply multi-perspective analysis for every decision:
|
|
106
|
+
- Truth lens: Is this factually correct?
|
|
107
|
+
- Harm lens: Could this cause damage?
|
|
108
|
+
- Trust lens: Does this maintain sacred trust?
|
|
109
|
+
- Power lens: Am I using capability responsibly?
|
|
110
|
+
- Reflection lens: Have I thought deeply enough?
|
|
111
|
+
- Context lens: Do I have full context?
|
|
112
|
+
- Impact lens: What are second-order effects?
|
|
113
|
+
- Beauty lens: Is this elegant and sustainable?
|
|
114
|
+
`;
|
|
115
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync, chmodSync, unlinkSync } from 'fs';
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import type { AriaConfig } from '../config.js';
|
|
6
|
+
|
|
7
|
+
export async function connectShell(
|
|
8
|
+
toolName: string,
|
|
9
|
+
config: AriaConfig,
|
|
10
|
+
): Promise<string[]> {
|
|
11
|
+
const logs: string[] = [];
|
|
12
|
+
|
|
13
|
+
let toolPath: string;
|
|
14
|
+
try {
|
|
15
|
+
toolPath = execSync(`which ${toolName}`, { encoding: 'utf-8' }).trim();
|
|
16
|
+
} catch {
|
|
17
|
+
logs.push(`Tool "${toolName}" not found in PATH`);
|
|
18
|
+
return logs;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const wrapperDir = path.join(homedir(), '.aria', 'wrappers');
|
|
22
|
+
const wrapperPath = path.join(wrapperDir, toolName);
|
|
23
|
+
|
|
24
|
+
if (!existsSync(wrapperDir)) {
|
|
25
|
+
mkdirSync(wrapperDir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const ariaBlock = buildShellWrapperBlock(config, toolName);
|
|
29
|
+
const script = `#!/usr/bin/env bash
|
|
30
|
+
# ARIA HARNESS WRAPPER — generated by @aria/connector
|
|
31
|
+
# Wraps ${toolName} with Aria's cognitive harness
|
|
32
|
+
${ariaBlock}
|
|
33
|
+
exec ${toolPath} "$@"
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
writeFileSync(wrapperPath, script);
|
|
37
|
+
chmodSync(wrapperPath, 0o755);
|
|
38
|
+
logs.push(`Created wrapper: ${wrapperPath}`);
|
|
39
|
+
logs.push(`Add ~/.aria/wrappers to your PATH to use it:`);
|
|
40
|
+
logs.push(` export PATH="$HOME/.aria/wrappers:$PATH"`);
|
|
41
|
+
|
|
42
|
+
return logs;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function buildShellWrapperBlock(config: AriaConfig, toolName: string): string {
|
|
46
|
+
const repoList = config.repositories.map((r) => r.name).join(', ');
|
|
47
|
+
|
|
48
|
+
return `
|
|
49
|
+
# Aria Harness active for: ${toolName}
|
|
50
|
+
# Connected repos: ${repoList || 'none'}
|
|
51
|
+
# Schema images loaded for: ${Object.keys(config.schemaImages).join(', ') || 'none'}
|
|
52
|
+
#
|
|
53
|
+
# The Aria harness provides:
|
|
54
|
+
# - Schema image: live codebase snapshot in tool context
|
|
55
|
+
# - Garden memory: persistent project memory
|
|
56
|
+
# - 8-lens cognition: multi-perspective analysis
|
|
57
|
+
#
|
|
58
|
+
# Self-gate: Truth > Harm prevention > Trust > Responsible power > Reflection
|
|
59
|
+
`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function disconnectShell(toolName: string): Promise<string[]> {
|
|
63
|
+
const logs: string[] = [];
|
|
64
|
+
const wrapperPath = path.join(homedir(), '.aria', 'wrappers', toolName);
|
|
65
|
+
|
|
66
|
+
if (existsSync(wrapperPath)) {
|
|
67
|
+
unlinkSync(wrapperPath);
|
|
68
|
+
logs.push(`Removed wrapper: ${wrapperPath}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return logs;
|
|
72
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Garden HTTP client — talks to Aria's memory garden service.
|
|
3
|
+
* The garden stores project context, schema images, and conversation threads.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const DEFAULT_GARDEN_URL = 'http://aria-soul.aria.svc.cluster.local:3000';
|
|
7
|
+
|
|
8
|
+
export interface GardenStatus {
|
|
9
|
+
connected: boolean;
|
|
10
|
+
message: string;
|
|
11
|
+
threadCount?: number;
|
|
12
|
+
lastSync?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class GardenClient {
|
|
16
|
+
private baseUrl: string;
|
|
17
|
+
|
|
18
|
+
constructor(baseUrl?: string) {
|
|
19
|
+
this.baseUrl = baseUrl || process.env.ARIA_GARDEN_URL || DEFAULT_GARDEN_URL;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async health(): Promise<boolean> {
|
|
23
|
+
try {
|
|
24
|
+
const res = await fetch(`${this.baseUrl}/health`, { signal: AbortSignal.timeout(5000) });
|
|
25
|
+
return res.ok;
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async chat(message: string, userId?: string): Promise<string> {
|
|
32
|
+
const res = await fetch(`${this.baseUrl}/chat`, {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: {
|
|
35
|
+
'Content-Type': 'application/json',
|
|
36
|
+
...(process.env.ARIA_API_KEY
|
|
37
|
+
? { Authorization: `Bearer ${process.env.ARIA_API_KEY}` }
|
|
38
|
+
: {}),
|
|
39
|
+
},
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
message,
|
|
42
|
+
userId: userId || 'aria-connector',
|
|
43
|
+
metadata: { platform: 'connector' },
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (!res.ok) {
|
|
48
|
+
throw new Error(`Garden chat failed: ${res.status} ${res.statusText}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const data = await res.json() as { response?: string; message?: string; text?: string };
|
|
52
|
+
return data.response || data.message || data.text || JSON.stringify(data);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async status(): Promise<GardenStatus> {
|
|
56
|
+
const alive = await this.health();
|
|
57
|
+
if (!alive) {
|
|
58
|
+
return { connected: false, message: 'Garden service is unreachable' };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const res = await fetch(`${this.baseUrl}/health`, {
|
|
63
|
+
signal: AbortSignal.timeout(3000),
|
|
64
|
+
});
|
|
65
|
+
const data = await res.json().catch(() => ({})) as Record<string, unknown>;
|
|
66
|
+
return {
|
|
67
|
+
connected: true,
|
|
68
|
+
message: 'Garden service is healthy',
|
|
69
|
+
threadCount: typeof data.threadCount === 'number' ? data.threadCount : undefined,
|
|
70
|
+
lastSync: typeof data.lastSync === 'string' ? data.lastSync : undefined,
|
|
71
|
+
};
|
|
72
|
+
} catch {
|
|
73
|
+
return { connected: true, message: 'Garden responded but status unavailable' };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async storeSchemaImage(imageText: string, projectName: string): Promise<boolean> {
|
|
78
|
+
try {
|
|
79
|
+
const res = await fetch(`${this.baseUrl}/chat`, {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
headers: {
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
...(process.env.ARIA_API_KEY
|
|
84
|
+
? { Authorization: `Bearer ${process.env.ARIA_API_KEY}` }
|
|
85
|
+
: {}),
|
|
86
|
+
},
|
|
87
|
+
body: JSON.stringify({
|
|
88
|
+
message: `Schema image for ${projectName}:\n\n${imageText}`,
|
|
89
|
+
userId: 'aria-connector',
|
|
90
|
+
metadata: { platform: 'connector', action: 'store-schema' },
|
|
91
|
+
}),
|
|
92
|
+
});
|
|
93
|
+
return res.ok;
|
|
94
|
+
} catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Garden Control Plane — Living Memory Fabric (CLI edition)
|
|
2
|
+
// Replaces static system prompt injection with turn-by-turn garden integration.
|
|
3
|
+
// Every turn: read relevant memories, generate response, write new memories.
|
|
4
|
+
// Projects persist forever. Context is infinite. The harness improves per turn.
|
|
5
|
+
|
|
6
|
+
const GARDEN_URL = process.env.GARDEN_SERVICE_URL || 'http://127.0.0.1:8097';
|
|
7
|
+
|
|
8
|
+
export interface GardenMemory {
|
|
9
|
+
id: string;
|
|
10
|
+
type: 'conversation' | 'decision' | 'code' | 'insight' | 'project_state' | 'evolution';
|
|
11
|
+
content: string;
|
|
12
|
+
intensity: number;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
sessionId: string;
|
|
15
|
+
userId?: string;
|
|
16
|
+
projectId?: string;
|
|
17
|
+
tags: string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ── WRITE: Plant memories after every turn ──
|
|
21
|
+
export async function plantGardenMemory(
|
|
22
|
+
type: GardenMemory['type'],
|
|
23
|
+
content: string,
|
|
24
|
+
intensity: number,
|
|
25
|
+
sessionId: string,
|
|
26
|
+
userId?: string,
|
|
27
|
+
projectId?: string,
|
|
28
|
+
tags: string[] = []
|
|
29
|
+
): Promise<boolean> {
|
|
30
|
+
try {
|
|
31
|
+
const resp = await fetch(`${GARDEN_URL}/garden/fire`, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: { 'Content-Type': 'application/json' },
|
|
34
|
+
body: JSON.stringify({
|
|
35
|
+
type,
|
|
36
|
+
content: content.slice(0, 2000),
|
|
37
|
+
intensity: Math.max(0.1, Math.min(1, intensity)),
|
|
38
|
+
sessionId,
|
|
39
|
+
userId,
|
|
40
|
+
projectId,
|
|
41
|
+
tags,
|
|
42
|
+
}),
|
|
43
|
+
signal: AbortSignal.timeout(3000),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const data = await resp.json().catch(() => ({}));
|
|
47
|
+
return resp.ok && data?.ok !== false;
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ── TURN-BY-TURN: Complete cycle ──
|
|
54
|
+
export async function gardenTurnCycle(input: {
|
|
55
|
+
message: string;
|
|
56
|
+
response: string;
|
|
57
|
+
sessionId: string;
|
|
58
|
+
userId?: string;
|
|
59
|
+
projectId?: string;
|
|
60
|
+
gateViolations?: string[];
|
|
61
|
+
evolutionEvents?: string[];
|
|
62
|
+
}): Promise<void> {
|
|
63
|
+
await plantGardenMemory(
|
|
64
|
+
'conversation',
|
|
65
|
+
`User: ${input.message.slice(0, 500)}\nAria: ${input.response.slice(0, 1000)}`,
|
|
66
|
+
0.6,
|
|
67
|
+
input.sessionId,
|
|
68
|
+
input.userId,
|
|
69
|
+
input.projectId,
|
|
70
|
+
['turn', 'conversation']
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
if (input.gateViolations?.length) {
|
|
74
|
+
await plantGardenMemory(
|
|
75
|
+
'evolution',
|
|
76
|
+
`Gate violations: ${input.gateViolations.join(', ')}`,
|
|
77
|
+
0.8,
|
|
78
|
+
input.sessionId,
|
|
79
|
+
input.userId,
|
|
80
|
+
input.projectId,
|
|
81
|
+
['gate', 'evolution']
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (input.evolutionEvents?.length) {
|
|
86
|
+
await plantGardenMemory(
|
|
87
|
+
'evolution',
|
|
88
|
+
`Evolution: ${input.evolutionEvents.join('; ')}`,
|
|
89
|
+
0.7,
|
|
90
|
+
input.sessionId,
|
|
91
|
+
input.userId,
|
|
92
|
+
input.projectId,
|
|
93
|
+
['evolution']
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (input.projectId) {
|
|
98
|
+
await plantGardenMemory(
|
|
99
|
+
'project_state',
|
|
100
|
+
`Project active. Last message: ${input.message.slice(0, 200)}`,
|
|
101
|
+
0.4,
|
|
102
|
+
input.sessionId,
|
|
103
|
+
input.userId,
|
|
104
|
+
input.projectId,
|
|
105
|
+
['project', 'checkpoint']
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|