brainclaw 0.19.11 → 0.19.14
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 +19 -0
- package/dist/cli.js +44 -1
- package/dist/commands/claude-desktop-extension.js +18 -0
- package/dist/commands/doctor.js +12 -5
- package/dist/commands/env.js +7 -1
- package/dist/commands/init.js +15 -0
- package/dist/commands/list-surface-tasks.js +39 -0
- package/dist/commands/mcp.js +7 -1
- package/dist/commands/reconcile.js +138 -0
- package/dist/commands/setup.js +19 -0
- package/dist/commands/status.js +17 -12
- package/dist/commands/surface-task-resource.js +35 -0
- package/dist/commands/surface-task.js +57 -0
- package/dist/commands/update-surface-task.js +30 -0
- package/dist/commands/version.js +3 -1
- package/dist/commands/whoami.js +11 -1
- package/dist/core/agent-files.js +18 -18
- package/dist/core/ai-surface-inventory.js +321 -0
- package/dist/core/ai-surface-tasks.js +40 -0
- package/dist/core/brainclaw-version.js +303 -62
- package/dist/core/claude-desktop-extension.js +224 -0
- package/dist/core/ids.js +1 -0
- package/dist/core/io.js +1 -0
- package/dist/core/machine-profile.js +7 -1
- package/dist/core/migration.js +3 -1
- package/dist/core/schema.js +25 -0
- package/dist/core/workspace-projects.js +115 -0
- package/docs/cli.md +141 -1
- package/docs/integrations/mcp.md +11 -6
- package/docs/quickstart.md +40 -0
- package/package.json +1 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { buildOperationalIdentity } from '../core/identity.js';
|
|
2
|
+
import { memoryExists } from '../core/io.js';
|
|
3
|
+
import { generateIdWithLabel, nowISO } from '../core/ids.js';
|
|
4
|
+
import { requireMinimumTrustLevel, requireRegisteredAgentIdentity } from '../core/agent-registry.js';
|
|
5
|
+
import { saveAiSurfaceTask } from '../core/ai-surface-tasks.js';
|
|
6
|
+
export function runSurfaceTask(title, options = {}) {
|
|
7
|
+
if (!memoryExists(options.cwd)) {
|
|
8
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
if (!options.target) {
|
|
12
|
+
console.error('Error: surface-task create requires --target <surface>.');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
if (!options.instructions?.trim()) {
|
|
16
|
+
console.error('Error: surface-task create requires --instructions <text>.');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const registeredAgent = requireRegisteredAgentIdentity({
|
|
20
|
+
agentName: options.agent,
|
|
21
|
+
agentId: options.agentId,
|
|
22
|
+
cwd: options.cwd,
|
|
23
|
+
allowCurrent: true,
|
|
24
|
+
allowEnv: true,
|
|
25
|
+
});
|
|
26
|
+
requireMinimumTrustLevel(registeredAgent, 'contributor');
|
|
27
|
+
const actor = buildOperationalIdentity(registeredAgent.agent_name, options.cwd, {
|
|
28
|
+
agentId: registeredAgent.agent_id,
|
|
29
|
+
});
|
|
30
|
+
const ids = generateIdWithLabel('ai_surface_tasks', options.cwd);
|
|
31
|
+
const timestamp = nowISO();
|
|
32
|
+
const task = {
|
|
33
|
+
id: ids.id,
|
|
34
|
+
short_label: ids.short_label,
|
|
35
|
+
title: title.trim(),
|
|
36
|
+
instructions: options.instructions.trim(),
|
|
37
|
+
target_surface: options.target.trim(),
|
|
38
|
+
kind: options.kind ?? 'custom',
|
|
39
|
+
created_at: timestamp,
|
|
40
|
+
updated_at: timestamp,
|
|
41
|
+
author: actor.agent,
|
|
42
|
+
author_id: actor.agent_id,
|
|
43
|
+
project_id: actor.project_id,
|
|
44
|
+
session_id: actor.session_id,
|
|
45
|
+
status: 'queued',
|
|
46
|
+
requested_outputs: options.output ?? [],
|
|
47
|
+
related_paths: options.path,
|
|
48
|
+
tags: options.tag ?? [],
|
|
49
|
+
};
|
|
50
|
+
saveAiSurfaceTask(task, options.cwd);
|
|
51
|
+
console.log(`✔ Surface task queued: [${task.id}] ${task.title}`);
|
|
52
|
+
console.log(` Target: ${task.target_surface} (${task.kind})`);
|
|
53
|
+
if (task.requested_outputs.length > 0) {
|
|
54
|
+
console.log(` Outputs: ${task.requested_outputs.join(', ')}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=surface-task.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { memoryExists } from '../core/io.js';
|
|
2
|
+
import { loadAiSurfaceTask, saveAiSurfaceTask } from '../core/ai-surface-tasks.js';
|
|
3
|
+
import { nowISO } from '../core/ids.js';
|
|
4
|
+
export function runUpdateSurfaceTask(id, options = {}) {
|
|
5
|
+
if (!memoryExists(options.cwd)) {
|
|
6
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
const task = loadAiSurfaceTask(id, options.cwd);
|
|
10
|
+
const timestamp = nowISO();
|
|
11
|
+
if (options.status) {
|
|
12
|
+
task.status = options.status;
|
|
13
|
+
if (options.status === 'in_progress' && !task.claimed_at) {
|
|
14
|
+
task.claimed_at = timestamp;
|
|
15
|
+
}
|
|
16
|
+
if ((options.status === 'completed' || options.status === 'failed' || options.status === 'cancelled') && !task.completed_at) {
|
|
17
|
+
task.completed_at = timestamp;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (options.result !== undefined) {
|
|
21
|
+
task.result_note = options.result;
|
|
22
|
+
}
|
|
23
|
+
if (options.output && options.output.length > 0) {
|
|
24
|
+
task.requested_outputs = options.output;
|
|
25
|
+
}
|
|
26
|
+
task.updated_at = timestamp;
|
|
27
|
+
saveAiSurfaceTask(task, options.cwd);
|
|
28
|
+
console.log(`✔ Surface task updated: [${task.id}] ${task.title}`);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=update-surface-task.js.map
|
package/dist/commands/version.js
CHANGED
|
@@ -31,7 +31,9 @@ export function runVersion(options = {}) {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
const assessment = assessBrainclawVersion(config);
|
|
34
|
-
const updateCheck = options.check
|
|
34
|
+
const updateCheck = options.check
|
|
35
|
+
? checkBrainclawInstallableUpdate(config, cwd, { useDefaultNpmSource: true })
|
|
36
|
+
: undefined;
|
|
35
37
|
const result = {
|
|
36
38
|
initialized,
|
|
37
39
|
...assessment,
|
package/dist/commands/whoami.js
CHANGED
|
@@ -4,7 +4,7 @@ import { loadConfig } from '../core/config.js';
|
|
|
4
4
|
import { resolveCurrentHostId } from '../core/host.js';
|
|
5
5
|
import { buildOperationalIdentity } from '../core/identity.js';
|
|
6
6
|
import { assessAgentIntegrationReadiness } from '../core/agent-integrations.js';
|
|
7
|
-
import { assessBrainclawVersion } from '../core/brainclaw-version.js';
|
|
7
|
+
import { assessBrainclawVersion, checkBrainclawInstallableUpdate, renderBrainclawInstallableUpdateNotice, } from '../core/brainclaw-version.js';
|
|
8
8
|
import { buildExecutionContext, compactExecutionContext } from '../core/execution-context.js';
|
|
9
9
|
import { buildAgentToolingContext } from '../core/agent-context.js';
|
|
10
10
|
export function runWhoami(options = {}) {
|
|
@@ -18,6 +18,8 @@ export function runWhoami(options = {}) {
|
|
|
18
18
|
const executionContext = compactExecutionContext(buildExecutionContext({ cwd }));
|
|
19
19
|
const integrationReadiness = assessAgentIntegrationReadiness(config, cwd);
|
|
20
20
|
const brainclawVersion = assessBrainclawVersion(config);
|
|
21
|
+
const installableUpdate = checkBrainclawInstallableUpdate(config, cwd, { useDefaultNpmSource: true });
|
|
22
|
+
const installableUpdateNotice = renderBrainclawInstallableUpdateNotice(installableUpdate);
|
|
21
23
|
const agentTooling = buildAgentToolingContext({ cwd });
|
|
22
24
|
let identity;
|
|
23
25
|
try {
|
|
@@ -45,6 +47,7 @@ export function runWhoami(options = {}) {
|
|
|
45
47
|
env_session: process.env.BRAINCLAW_SESSION_ID ?? null,
|
|
46
48
|
env_host: process.env.BRAINCLAW_HOST_ID ?? null,
|
|
47
49
|
brainclaw_version: brainclawVersion,
|
|
50
|
+
installable_update: installableUpdate,
|
|
48
51
|
declared_agent_integrations: config.agent_integrations,
|
|
49
52
|
integration_readiness: integrationReadiness,
|
|
50
53
|
execution_context: executionContext,
|
|
@@ -80,6 +83,13 @@ export function runWhoami(options = {}) {
|
|
|
80
83
|
if (result.brainclaw_version.status !== 'ok') {
|
|
81
84
|
console.log(` Version : ${result.brainclaw_version.message}`);
|
|
82
85
|
}
|
|
86
|
+
if (installableUpdateNotice) {
|
|
87
|
+
const lines = installableUpdateNotice.split('\n');
|
|
88
|
+
console.log(` Update : ${lines[0]}`);
|
|
89
|
+
for (const line of lines.slice(1)) {
|
|
90
|
+
console.log(` ${line}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
83
93
|
console.log(` Declared integrations: ${result.declared_agent_integrations.declarations.length}`);
|
|
84
94
|
const missingIntegrations = result.integration_readiness.filter((entry) => !entry.ready);
|
|
85
95
|
if (missingIntegrations.length > 0) {
|
package/dist/core/agent-files.js
CHANGED
|
@@ -13,21 +13,21 @@ This project uses brainclaw for shared coordination between humans and agents.
|
|
|
13
13
|
|
|
14
14
|
1. Run \`brainclaw context\` to load shared state (constraints, decisions, traps, plans, handoffs)
|
|
15
15
|
2. Check **Your open work** for active claims and in-progress plans assigned to you
|
|
16
|
-
3. Respect active claims from other agents — check \`brainclaw claim list\` before editing a claimed scope
|
|
16
|
+
3. Respect active claims from other agents — check \`brainclaw claim list\` before editing a claimed scope
|
|
17
17
|
|
|
18
18
|
### Before finishing (required)
|
|
19
19
|
|
|
20
|
-
1. Release claims you opened: \`brainclaw claim release <id>\` — or \`brainclaw session-end --auto-release\`
|
|
21
|
-
2. Update completed plan items: \`brainclaw plan update <id> --status done\`
|
|
20
|
+
1. Release claims you opened: \`brainclaw claim release <id>\` — or \`brainclaw session-end --auto-release\`
|
|
21
|
+
2. Update completed plan items: \`brainclaw plan update <id> --status done\`
|
|
22
22
|
|
|
23
23
|
### Recording work
|
|
24
24
|
|
|
25
25
|
\`\`\`bash
|
|
26
|
-
brainclaw memory create decision "<text>" # record a decision
|
|
27
|
-
brainclaw memory create constraint "<text>" # record an active constraint
|
|
28
|
-
brainclaw memory create trap "<text>" # record a known trap
|
|
29
|
-
brainclaw claim create "<text>" --scope <path> # claim a scope before editing
|
|
30
|
-
brainclaw plan create "<text>" # add a shared work item
|
|
26
|
+
brainclaw memory create decision "<text>" # record a decision
|
|
27
|
+
brainclaw memory create constraint "<text>" # record an active constraint
|
|
28
|
+
brainclaw memory create trap "<text>" # record a known trap
|
|
29
|
+
brainclaw claim create "<text>" --scope <path> # claim a scope before editing
|
|
30
|
+
brainclaw plan create "<text>" # add a shared work item
|
|
31
31
|
\`\`\`
|
|
32
32
|
|
|
33
33
|
Memory is stored in \`${storageDir}/\`. Run \`brainclaw doctor\` to verify health.
|
|
@@ -38,9 +38,9 @@ export function buildHygieneSection() {
|
|
|
38
38
|
|
|
39
39
|
Before starting work:
|
|
40
40
|
1. Run \`brainclaw context\` (or \`brainclaw context --json\`) to load shared memory
|
|
41
|
-
2. Run \`brainclaw claim list\` — do not edit a file claimed by another agent
|
|
42
|
-
3. Create a plan for significant work: \`brainclaw plan create "<description>"\`
|
|
43
|
-
4. Claim files you will modify: \`brainclaw claim create "<description>" --scope <path>\`
|
|
41
|
+
2. Run \`brainclaw claim list\` — do not edit a file claimed by another agent
|
|
42
|
+
3. Create a plan for significant work: \`brainclaw plan create "<description>"\`
|
|
43
|
+
4. Claim files you will modify: \`brainclaw claim create "<description>" --scope <path>\`
|
|
44
44
|
|
|
45
45
|
Before finishing:
|
|
46
46
|
1. Run \`brainclaw session-end --auto-release\` — releases claims and updates plans
|
|
@@ -327,13 +327,13 @@ export function describeAutoConfigWrite(result) {
|
|
|
327
327
|
return `✔ ${verb} ${result.label} at ${displayPath}`;
|
|
328
328
|
}
|
|
329
329
|
export function buildClaudeCodeCommandText() {
|
|
330
|
-
return `Load brainclaw project memory and prepare for coordinated work.
|
|
331
|
-
|
|
332
|
-
Steps:
|
|
333
|
-
1. Run \`brainclaw context --json\` — load constraints, decisions, traps, plans, handoffs
|
|
334
|
-
2. Run \`brainclaw claim list\` — check what files other agents have claimed
|
|
335
|
-
3. Before editing any file, run \`brainclaw claim create "<description>" --scope <path>\`
|
|
336
|
-
4. Before finishing, run \`brainclaw session-end --auto-release\`
|
|
330
|
+
return `Load brainclaw project memory and prepare for coordinated work.
|
|
331
|
+
|
|
332
|
+
Steps:
|
|
333
|
+
1. Run \`brainclaw context --json\` — load constraints, decisions, traps, plans, handoffs
|
|
334
|
+
2. Run \`brainclaw claim list\` — check what files other agents have claimed
|
|
335
|
+
3. Before editing any file, run \`brainclaw claim create "<description>" --scope <path>\`
|
|
336
|
+
4. Before finishing, run \`brainclaw session-end --auto-release\`
|
|
337
337
|
`;
|
|
338
338
|
}
|
|
339
339
|
export function ensureClineMcpConfig(cwd) {
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
function run(command, args, timeout = 5000) {
|
|
6
|
+
try {
|
|
7
|
+
const result = spawnSync(command, args, { encoding: 'utf-8', timeout, windowsHide: true });
|
|
8
|
+
return { ok: result.status === 0, stdout: result.stdout ?? '', stderr: result.stderr ?? '' };
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return { ok: false, stdout: '', stderr: '' };
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function listRunningProcesses(platform) {
|
|
15
|
+
if (platform === 'win32') {
|
|
16
|
+
const result = run('tasklist', ['/FO', 'CSV', '/NH'], 8000);
|
|
17
|
+
if (!result.ok)
|
|
18
|
+
return [];
|
|
19
|
+
return result.stdout
|
|
20
|
+
.split(/\r?\n/)
|
|
21
|
+
.map((line) => line.trim())
|
|
22
|
+
.filter(Boolean)
|
|
23
|
+
.map((line) => line.replace(/^"|"$/g, '').split('","')[0] ?? '')
|
|
24
|
+
.map((name) => name.replace(/\.exe$/i, '').toLowerCase())
|
|
25
|
+
.filter(Boolean);
|
|
26
|
+
}
|
|
27
|
+
const result = run('ps', ['-A', '-o', 'comm='], 8000);
|
|
28
|
+
if (!result.ok)
|
|
29
|
+
return [];
|
|
30
|
+
return result.stdout
|
|
31
|
+
.split(/\r?\n/)
|
|
32
|
+
.map((line) => path.basename(line.trim()).toLowerCase())
|
|
33
|
+
.filter(Boolean);
|
|
34
|
+
}
|
|
35
|
+
function detectWindowsAppxPackages() {
|
|
36
|
+
if (process.platform !== 'win32')
|
|
37
|
+
return [];
|
|
38
|
+
const script = [
|
|
39
|
+
"$patterns = @('OpenAI.ChatGPT-Desktop', '*ChatGPT*', 'Claude', '*Claude*');",
|
|
40
|
+
'$packages = foreach ($pattern in $patterns) { Get-AppxPackage -Name $pattern -ErrorAction SilentlyContinue };',
|
|
41
|
+
'$packages | Sort-Object Name -Unique |',
|
|
42
|
+
'ForEach-Object { "{0}`t{1}`t{2}" -f $_.Name, $_.Version.ToString(), $_.InstallLocation }',
|
|
43
|
+
].join(' ');
|
|
44
|
+
const result = run('powershell', ['-NoProfile', '-Command', script], 15000);
|
|
45
|
+
if (!result.ok || !result.stdout.trim())
|
|
46
|
+
return [];
|
|
47
|
+
return result.stdout
|
|
48
|
+
.split(/\r?\n/)
|
|
49
|
+
.map((line) => line.trim())
|
|
50
|
+
.filter(Boolean)
|
|
51
|
+
.map((line) => {
|
|
52
|
+
const [name, version, installLocation] = line.split('\t');
|
|
53
|
+
return {
|
|
54
|
+
name: name ?? '',
|
|
55
|
+
version: version || undefined,
|
|
56
|
+
installLocation: installLocation || undefined,
|
|
57
|
+
};
|
|
58
|
+
})
|
|
59
|
+
.filter((row) => row.name);
|
|
60
|
+
}
|
|
61
|
+
function detectBrowsers(homeDir, platform) {
|
|
62
|
+
const browsers = new Set();
|
|
63
|
+
const commands = platform === 'win32'
|
|
64
|
+
? ['msedge', 'chrome', 'firefox']
|
|
65
|
+
: platform === 'darwin'
|
|
66
|
+
? ['open', 'google-chrome', 'firefox', 'safari']
|
|
67
|
+
: ['xdg-open', 'google-chrome', 'chromium-browser', 'chromium', 'firefox'];
|
|
68
|
+
for (const command of commands) {
|
|
69
|
+
const result = run(platform === 'win32' ? 'where' : 'which', [command], 3000);
|
|
70
|
+
if (result.ok)
|
|
71
|
+
browsers.add(command);
|
|
72
|
+
}
|
|
73
|
+
const footprintPaths = platform === 'win32'
|
|
74
|
+
? [
|
|
75
|
+
path.join(process.env.LOCALAPPDATA ?? '', 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
|
|
76
|
+
path.join(process.env.LOCALAPPDATA ?? '', 'Google', 'Chrome', 'Application', 'chrome.exe'),
|
|
77
|
+
path.join(process.env.PROGRAMFILES ?? 'C:\\Program Files', 'Mozilla Firefox', 'firefox.exe'),
|
|
78
|
+
]
|
|
79
|
+
: platform === 'darwin'
|
|
80
|
+
? [
|
|
81
|
+
'/Applications/Google Chrome.app',
|
|
82
|
+
'/Applications/Firefox.app',
|
|
83
|
+
'/Applications/Safari.app',
|
|
84
|
+
path.join(homeDir, 'Applications', 'Google Chrome.app'),
|
|
85
|
+
]
|
|
86
|
+
: [
|
|
87
|
+
path.join(homeDir, '.local', 'share', 'applications'),
|
|
88
|
+
'/usr/share/applications',
|
|
89
|
+
];
|
|
90
|
+
for (const footprint of footprintPaths) {
|
|
91
|
+
if (fs.existsSync(footprint))
|
|
92
|
+
browsers.add(path.basename(footprint).toLowerCase());
|
|
93
|
+
}
|
|
94
|
+
return [...browsers];
|
|
95
|
+
}
|
|
96
|
+
function matchProcess(processNames, pattern) {
|
|
97
|
+
return processNames.some((name) => pattern.test(name));
|
|
98
|
+
}
|
|
99
|
+
function findExistingPath(paths) {
|
|
100
|
+
return paths.find((candidate) => candidate && fs.existsSync(candidate));
|
|
101
|
+
}
|
|
102
|
+
function matchWindowsAppxPackage(packages, pattern) {
|
|
103
|
+
return packages.find((pkg) => pattern.test(pkg.name));
|
|
104
|
+
}
|
|
105
|
+
export function buildAiSurfaceInventory(options = {}) {
|
|
106
|
+
const platform = options.platform ?? process.platform;
|
|
107
|
+
const homeDir = options.homeDir ?? os.homedir();
|
|
108
|
+
const processNames = options.processNames ?? listRunningProcesses(platform);
|
|
109
|
+
const windowsAppxPackages = options.windowsAppxPackages ?? (platform === 'win32' ? detectWindowsAppxPackages() : []);
|
|
110
|
+
const browsers = options.browsers ?? detectBrowsers(homeDir, platform);
|
|
111
|
+
const surfaces = [];
|
|
112
|
+
const chatGptAppx = platform === 'win32'
|
|
113
|
+
? matchWindowsAppxPackage(windowsAppxPackages, /OpenAI\.ChatGPT-Desktop|ChatGPT/i)
|
|
114
|
+
: undefined;
|
|
115
|
+
const chatGptRunning = matchProcess(processNames, /^chatgpt$/i);
|
|
116
|
+
const chatGptInstallLocation = platform === 'darwin'
|
|
117
|
+
? findExistingPath([
|
|
118
|
+
'/Applications/ChatGPT.app',
|
|
119
|
+
path.join(homeDir, 'Applications', 'ChatGPT.app'),
|
|
120
|
+
])
|
|
121
|
+
: platform === 'win32'
|
|
122
|
+
? chatGptAppx?.installLocation
|
|
123
|
+
: undefined;
|
|
124
|
+
const chatGptDetected = Boolean(chatGptRunning || chatGptInstallLocation || chatGptAppx);
|
|
125
|
+
surfaces.push({
|
|
126
|
+
id: `surf_chatgpt_${platform}`,
|
|
127
|
+
product_name: 'chatgpt',
|
|
128
|
+
display_name: 'ChatGPT Desktop',
|
|
129
|
+
surface_kind: platform === 'linux' ? 'web_surface' : 'desktop_ai_app',
|
|
130
|
+
variant: platform === 'win32' ? 'windows_store' : platform === 'darwin' ? 'macos_app' : 'web',
|
|
131
|
+
status: chatGptRunning ? 'detected_running' : chatGptDetected ? 'detected_install' : (platform === 'linux' && browsers.length > 0 ? 'limited' : 'not_detected'),
|
|
132
|
+
running: chatGptRunning,
|
|
133
|
+
install_source: chatGptAppx ? 'appx' : chatGptInstallLocation ? 'bundle' : platform === 'linux' && browsers.length > 0 ? 'web' : undefined,
|
|
134
|
+
install_location: chatGptInstallLocation,
|
|
135
|
+
version: chatGptAppx?.version,
|
|
136
|
+
detection_sources: [
|
|
137
|
+
...(chatGptAppx ? [`AppX package: ${chatGptAppx.name}`] : []),
|
|
138
|
+
...(chatGptInstallLocation ? [`install path: ${chatGptInstallLocation}`] : []),
|
|
139
|
+
...(chatGptRunning ? ['running process: ChatGPT'] : []),
|
|
140
|
+
...(platform === 'linux' && browsers.length > 0 ? ['browser availability'] : []),
|
|
141
|
+
],
|
|
142
|
+
supports_mcp: 'unknown',
|
|
143
|
+
supports_remote_connectors: 'unknown',
|
|
144
|
+
supports_local_config: 'limited',
|
|
145
|
+
supports_context_export: 'yes',
|
|
146
|
+
supports_prompt_bootstrap: 'yes',
|
|
147
|
+
supports_safe_write_actions: 'limited',
|
|
148
|
+
interactive_only: true,
|
|
149
|
+
can_edit_code: false,
|
|
150
|
+
recommended_uses: [
|
|
151
|
+
'generate visual concepts and rough assets',
|
|
152
|
+
'draft product copy and polished summaries',
|
|
153
|
+
'prepare slide, email, or launch material from project context',
|
|
154
|
+
],
|
|
155
|
+
});
|
|
156
|
+
const claudeRunning = matchProcess(processNames, /^claude$/i);
|
|
157
|
+
const claudeAppx = platform === 'win32'
|
|
158
|
+
? matchWindowsAppxPackage(windowsAppxPackages, /^Claude$/i)
|
|
159
|
+
: undefined;
|
|
160
|
+
const claudeInstallLocation = platform === 'darwin'
|
|
161
|
+
? findExistingPath([
|
|
162
|
+
'/Applications/Claude.app',
|
|
163
|
+
path.join(homeDir, 'Applications', 'Claude.app'),
|
|
164
|
+
])
|
|
165
|
+
: platform === 'win32'
|
|
166
|
+
? (claudeAppx?.installLocation ?? findExistingPath([
|
|
167
|
+
path.join(process.env.LOCALAPPDATA ?? '', 'Programs', 'Claude', 'Claude.exe'),
|
|
168
|
+
path.join(process.env.LOCALAPPDATA ?? '', 'AnthropicClaude', 'Claude.exe'),
|
|
169
|
+
path.join(process.env.LOCALAPPDATA ?? '', 'Claude'),
|
|
170
|
+
path.join(process.env.APPDATA ?? '', 'Claude'),
|
|
171
|
+
]))
|
|
172
|
+
: undefined;
|
|
173
|
+
const claudeDetected = Boolean(claudeRunning || claudeInstallLocation);
|
|
174
|
+
surfaces.push({
|
|
175
|
+
id: `surf_claude_desktop_${platform}`,
|
|
176
|
+
product_name: 'claude',
|
|
177
|
+
display_name: 'Claude Desktop',
|
|
178
|
+
surface_kind: platform === 'linux' ? 'web_surface' : 'desktop_ai_app',
|
|
179
|
+
variant: platform === 'darwin' ? 'macos_app' : platform === 'win32' ? 'desktop' : 'web',
|
|
180
|
+
status: claudeRunning ? 'detected_running' : claudeDetected ? 'detected_install' : (platform === 'linux' && browsers.length > 0 ? 'limited' : 'not_detected'),
|
|
181
|
+
running: claudeRunning,
|
|
182
|
+
install_source: claudeAppx ? 'appx' : claudeInstallLocation ? 'bundle' : platform === 'linux' && browsers.length > 0 ? 'web' : undefined,
|
|
183
|
+
install_location: claudeInstallLocation,
|
|
184
|
+
version: claudeAppx?.version,
|
|
185
|
+
detection_sources: [
|
|
186
|
+
...(claudeAppx ? [`AppX package: ${claudeAppx.name}`] : []),
|
|
187
|
+
...(claudeInstallLocation ? [`install path: ${claudeInstallLocation}`] : []),
|
|
188
|
+
...(claudeRunning ? ['running process: Claude'] : []),
|
|
189
|
+
...(platform === 'linux' && browsers.length > 0 ? ['browser availability'] : []),
|
|
190
|
+
],
|
|
191
|
+
supports_mcp: platform === 'linux' ? 'limited' : 'yes',
|
|
192
|
+
supports_remote_connectors: platform === 'linux' ? 'limited' : 'yes',
|
|
193
|
+
supports_local_config: platform === 'linux' ? 'limited' : 'yes',
|
|
194
|
+
supports_context_export: 'yes',
|
|
195
|
+
supports_prompt_bootstrap: 'yes',
|
|
196
|
+
supports_safe_write_actions: 'limited',
|
|
197
|
+
interactive_only: true,
|
|
198
|
+
can_edit_code: false,
|
|
199
|
+
recommended_uses: [
|
|
200
|
+
'project synthesis and reasoning-heavy drafting',
|
|
201
|
+
'doc and handoff preparation around a repo',
|
|
202
|
+
'MCP-oriented project context consumption when supported',
|
|
203
|
+
],
|
|
204
|
+
});
|
|
205
|
+
surfaces.push({
|
|
206
|
+
id: `surf_claude_cowork_${platform}`,
|
|
207
|
+
product_name: 'claude-cowork',
|
|
208
|
+
display_name: 'Claude Cowork',
|
|
209
|
+
surface_kind: 'desktop_embedded_capability',
|
|
210
|
+
variant: 'embedded',
|
|
211
|
+
parent_surface_id: `surf_claude_desktop_${platform}`,
|
|
212
|
+
status: claudeDetected || claudeRunning ? 'limited' : 'not_detected',
|
|
213
|
+
running: false,
|
|
214
|
+
detection_sources: claudeDetected || claudeRunning
|
|
215
|
+
? ['parent capability: Claude Desktop']
|
|
216
|
+
: [],
|
|
217
|
+
supports_mcp: 'limited',
|
|
218
|
+
supports_remote_connectors: 'limited',
|
|
219
|
+
supports_local_config: 'limited',
|
|
220
|
+
supports_context_export: 'yes',
|
|
221
|
+
supports_prompt_bootstrap: 'yes',
|
|
222
|
+
supports_safe_write_actions: 'limited',
|
|
223
|
+
interactive_only: true,
|
|
224
|
+
can_edit_code: false,
|
|
225
|
+
recommended_uses: [
|
|
226
|
+
'parallel collaboration on non-code deliverables',
|
|
227
|
+
'task follow-up and structured handoff work',
|
|
228
|
+
'operator-facing drafting without repo edits',
|
|
229
|
+
],
|
|
230
|
+
});
|
|
231
|
+
const geminiCliPath = findExistingPath([
|
|
232
|
+
path.join(homeDir, '.gemini', 'antigravity'),
|
|
233
|
+
]);
|
|
234
|
+
const geminiCliRunning = matchProcess(processNames, /gemini|antigravity/i);
|
|
235
|
+
const geminiCliVersion = run('gemini', ['--version'], 3000);
|
|
236
|
+
const geminiCliDetected = Boolean(geminiCliPath || geminiCliRunning || geminiCliVersion.ok);
|
|
237
|
+
surfaces.push({
|
|
238
|
+
id: `surf_gemini_cli_${platform}`,
|
|
239
|
+
product_name: 'gemini',
|
|
240
|
+
display_name: 'Gemini CLI / Antigravity',
|
|
241
|
+
surface_kind: 'cli_agent',
|
|
242
|
+
variant: 'antigravity',
|
|
243
|
+
status: geminiCliRunning ? 'detected_running' : geminiCliDetected ? 'brainclaw_ready' : 'not_detected',
|
|
244
|
+
running: geminiCliRunning,
|
|
245
|
+
install_source: geminiCliPath ? 'config_footprint' : geminiCliVersion.ok ? 'cli' : undefined,
|
|
246
|
+
install_location: geminiCliPath,
|
|
247
|
+
version: geminiCliVersion.ok ? geminiCliVersion.stdout.trim() : undefined,
|
|
248
|
+
detection_sources: [
|
|
249
|
+
...(geminiCliPath ? [`config path: ${geminiCliPath}`] : []),
|
|
250
|
+
...(geminiCliVersion.ok ? ['gemini --version'] : []),
|
|
251
|
+
...(geminiCliRunning ? ['running process: gemini/antigravity'] : []),
|
|
252
|
+
],
|
|
253
|
+
supports_mcp: 'yes',
|
|
254
|
+
supports_remote_connectors: 'unknown',
|
|
255
|
+
supports_local_config: 'yes',
|
|
256
|
+
supports_context_export: 'yes',
|
|
257
|
+
supports_prompt_bootstrap: 'yes',
|
|
258
|
+
supports_safe_write_actions: 'limited',
|
|
259
|
+
interactive_only: false,
|
|
260
|
+
can_edit_code: false,
|
|
261
|
+
recommended_uses: [
|
|
262
|
+
'CLI-driven analysis and automation-adjacent tasks',
|
|
263
|
+
'repo-adjacent reasoning without changing the editing agent',
|
|
264
|
+
'structured use of local MCP and exported context',
|
|
265
|
+
],
|
|
266
|
+
});
|
|
267
|
+
const geminiWebAvailable = browsers.length > 0;
|
|
268
|
+
surfaces.push({
|
|
269
|
+
id: `surf_gemini_web_${platform}`,
|
|
270
|
+
product_name: 'gemini',
|
|
271
|
+
display_name: 'Gemini Web',
|
|
272
|
+
surface_kind: 'web_surface',
|
|
273
|
+
variant: 'browser',
|
|
274
|
+
status: geminiWebAvailable ? 'limited' : 'not_detected',
|
|
275
|
+
running: matchProcess(processNames, /chrome|msedge|firefox|safari/i),
|
|
276
|
+
install_source: geminiWebAvailable ? 'web' : undefined,
|
|
277
|
+
detection_sources: geminiWebAvailable ? [`browser availability: ${browsers.join(', ')}`] : [],
|
|
278
|
+
supports_mcp: 'unknown',
|
|
279
|
+
supports_remote_connectors: 'unknown',
|
|
280
|
+
supports_local_config: 'limited',
|
|
281
|
+
supports_context_export: 'yes',
|
|
282
|
+
supports_prompt_bootstrap: 'yes',
|
|
283
|
+
supports_safe_write_actions: 'limited',
|
|
284
|
+
interactive_only: true,
|
|
285
|
+
can_edit_code: false,
|
|
286
|
+
recommended_uses: [
|
|
287
|
+
'quick research and synthesis in a browser surface',
|
|
288
|
+
'lightweight planning, summaries, and exploration',
|
|
289
|
+
'prompt-bootstrap workflows when no native integration exists',
|
|
290
|
+
],
|
|
291
|
+
});
|
|
292
|
+
return surfaces;
|
|
293
|
+
}
|
|
294
|
+
export function renderAiSurfaceSummary(surfaces) {
|
|
295
|
+
const detected = surfaces.filter((surface) => surface.status !== 'not_detected');
|
|
296
|
+
const lines = [];
|
|
297
|
+
lines.push(`AI surfaces: ${detected.length}/${surfaces.length} detected or available`);
|
|
298
|
+
for (const surface of detected) {
|
|
299
|
+
const details = [surface.surface_kind, surface.status];
|
|
300
|
+
if (surface.variant)
|
|
301
|
+
details.push(surface.variant);
|
|
302
|
+
if (surface.version)
|
|
303
|
+
details.push(surface.version);
|
|
304
|
+
lines.push(` - ${surface.display_name} (${details.join(', ')})`);
|
|
305
|
+
}
|
|
306
|
+
return lines;
|
|
307
|
+
}
|
|
308
|
+
export function renderAiSurfaceUsageHints(surfaces) {
|
|
309
|
+
const eligible = surfaces.filter((surface) => surface.status !== 'not_detected');
|
|
310
|
+
const lines = [];
|
|
311
|
+
for (const surface of eligible) {
|
|
312
|
+
if (surface.recommended_uses.length === 0)
|
|
313
|
+
continue;
|
|
314
|
+
lines.push(`${surface.display_name}:`);
|
|
315
|
+
for (const useCase of surface.recommended_uses.slice(0, 2)) {
|
|
316
|
+
lines.push(` - ${useCase}`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return lines;
|
|
320
|
+
}
|
|
321
|
+
//# sourceMappingURL=ai-surface-inventory.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { JsonStore } from './json-store.js';
|
|
3
|
+
import { resolveEntityDir, withStoreLock } from './io.js';
|
|
4
|
+
import { AiSurfaceTaskRequestSchema } from './schema.js';
|
|
5
|
+
function surfaceTasksDir(cwd, mode = 'read') {
|
|
6
|
+
return resolveEntityDir('surface-tasks', cwd ?? process.cwd(), mode);
|
|
7
|
+
}
|
|
8
|
+
function surfaceTaskStore(cwd) {
|
|
9
|
+
return new JsonStore({
|
|
10
|
+
dirPath: surfaceTasksDir(cwd, 'read'),
|
|
11
|
+
documentType: 'ai_surface_task',
|
|
12
|
+
getId: (task) => task.id,
|
|
13
|
+
sort: (a, b) => a.created_at.localeCompare(b.created_at),
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export function ensureAiSurfaceTasksDir(cwd) {
|
|
17
|
+
const dir = surfaceTasksDir(cwd, 'write');
|
|
18
|
+
if (!fs.existsSync(dir)) {
|
|
19
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function saveAiSurfaceTask(task, cwd) {
|
|
23
|
+
withStoreLock(cwd, () => {
|
|
24
|
+
ensureAiSurfaceTasksDir(cwd);
|
|
25
|
+
const writeStore = new JsonStore({
|
|
26
|
+
dirPath: surfaceTasksDir(cwd, 'write'),
|
|
27
|
+
documentType: 'ai_surface_task',
|
|
28
|
+
getId: (entry) => entry.id,
|
|
29
|
+
sort: (a, b) => a.created_at.localeCompare(b.created_at),
|
|
30
|
+
});
|
|
31
|
+
writeStore.save(AiSurfaceTaskRequestSchema.parse(task));
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export function loadAiSurfaceTask(id, cwd) {
|
|
35
|
+
return surfaceTaskStore(cwd).load(id);
|
|
36
|
+
}
|
|
37
|
+
export function listAiSurfaceTasks(cwd) {
|
|
38
|
+
return surfaceTaskStore(cwd).list();
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=ai-surface-tasks.js.map
|