@askexenow/exe-os 0.8.37 → 0.8.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -8
- package/dist/bin/backfill-conversations.js +112 -70
- package/dist/bin/backfill-responses.js +53 -18
- package/dist/bin/backfill-vectors.js +43 -16
- package/dist/bin/cleanup-stale-review-tasks.js +38 -16
- package/dist/bin/cli.js +790 -468
- package/dist/bin/exe-agent.js +19 -4
- package/dist/bin/exe-assign.js +46 -13
- package/dist/bin/exe-boot.js +288 -129
- package/dist/bin/exe-call.js +20 -10
- package/dist/bin/exe-cloud.js +135 -30
- package/dist/bin/exe-dispatch.js +1 -1
- package/dist/bin/exe-doctor.js +38 -16
- package/dist/bin/exe-export-behaviors.js +43 -21
- package/dist/bin/exe-forget.js +39 -17
- package/dist/bin/exe-gateway.js +159 -50
- package/dist/bin/exe-heartbeat.js +53 -31
- package/dist/bin/exe-kill.js +40 -18
- package/dist/bin/exe-launch-agent.js +109 -36
- package/dist/bin/exe-link.js +196 -87
- package/dist/bin/exe-new-employee.js +56 -17
- package/dist/bin/exe-pending-messages.js +47 -25
- package/dist/bin/exe-pending-notifications.js +38 -16
- package/dist/bin/exe-pending-reviews.js +51 -29
- package/dist/bin/exe-rename.js +21 -7
- package/dist/bin/exe-review.js +41 -13
- package/dist/bin/exe-search.js +57 -21
- package/dist/bin/exe-session-cleanup.js +67 -31
- package/dist/bin/exe-settings.js +63 -2
- package/dist/bin/exe-status.js +35 -13
- package/dist/bin/exe-team.js +35 -13
- package/dist/bin/git-sweep.js +45 -17
- package/dist/bin/graph-backfill.js +38 -16
- package/dist/bin/graph-export.js +38 -16
- package/dist/bin/install.js +10 -1
- package/dist/bin/scan-tasks.js +47 -19
- package/dist/bin/setup.js +444 -259
- package/dist/bin/shard-migrate.js +38 -16
- package/dist/bin/wiki-sync.js +40 -17
- package/dist/gateway/index.js +113 -48
- package/dist/hooks/bug-report-worker.js +66 -39
- package/dist/hooks/commit-complete.js +45 -17
- package/dist/hooks/error-recall.js +60 -20
- package/dist/hooks/exe-heartbeat-hook.js +3 -2
- package/dist/hooks/ingest-worker.js +174 -45
- package/dist/hooks/ingest.js +74 -28
- package/dist/hooks/instructions-loaded.js +46 -17
- package/dist/hooks/notification.js +44 -15
- package/dist/hooks/post-compact.js +44 -15
- package/dist/hooks/pre-compact.js +42 -14
- package/dist/hooks/pre-tool-use.js +59 -22
- package/dist/hooks/prompt-ingest-worker.js +75 -14
- package/dist/hooks/prompt-submit.js +75 -32
- package/dist/hooks/response-ingest-worker.js +76 -15
- package/dist/hooks/session-end.js +54 -22
- package/dist/hooks/session-start.js +57 -20
- package/dist/hooks/stop.js +44 -15
- package/dist/hooks/subagent-stop.js +44 -15
- package/dist/hooks/summary-worker.js +339 -106
- package/dist/index.js +94 -23
- package/dist/lib/cloud-sync.js +191 -80
- package/dist/lib/config.js +4 -1
- package/dist/lib/consolidation.js +5 -4
- package/dist/lib/database.js +1 -0
- package/dist/lib/device-registry.js +2 -1
- package/dist/lib/embedder.js +9 -1
- package/dist/lib/employee-templates.js +5 -0
- package/dist/lib/employees.js +11 -6
- package/dist/lib/exe-daemon-client.js +6 -1
- package/dist/lib/exe-daemon.js +95 -36
- package/dist/lib/hybrid-search.js +57 -21
- package/dist/lib/identity-templates.js +16 -7
- package/dist/lib/identity.js +1 -1
- package/dist/lib/keychain.js +2 -1
- package/dist/lib/license.js +56 -6
- package/dist/lib/messaging.js +1 -1
- package/dist/lib/reminders.js +2 -2
- package/dist/lib/schedules.js +38 -16
- package/dist/lib/skill-learning.js +1 -1
- package/dist/lib/store.js +44 -16
- package/dist/lib/tasks.js +1 -1
- package/dist/lib/tmux-routing.js +1 -1
- package/dist/mcp/server.js +280 -155
- package/dist/mcp/tools/complete-reminder.js +1 -1
- package/dist/mcp/tools/create-task.js +14 -6
- package/dist/mcp/tools/deactivate-behavior.js +2 -2
- package/dist/mcp/tools/list-reminders.js +1 -1
- package/dist/mcp/tools/list-tasks.js +36 -28
- package/dist/mcp/tools/send-message.js +1 -1
- package/dist/mcp/tools/update-task.js +1 -1
- package/dist/runtime/index.js +42 -8
- package/dist/tui/App.js +220 -99
- package/package.json +5 -3
package/dist/bin/setup.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
+
}) : x)(function(x) {
|
|
7
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
+
});
|
|
4
10
|
var __esm = (fn, res) => function __init() {
|
|
5
11
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
12
|
};
|
|
@@ -25,7 +31,7 @@ __export(config_exports, {
|
|
|
25
31
|
migrateConfig: () => migrateConfig,
|
|
26
32
|
saveConfig: () => saveConfig
|
|
27
33
|
});
|
|
28
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
34
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
29
35
|
import { readFileSync, existsSync, renameSync } from "fs";
|
|
30
36
|
import path from "path";
|
|
31
37
|
import os from "os";
|
|
@@ -151,6 +157,9 @@ async function saveConfig(config) {
|
|
|
151
157
|
await mkdir(dir, { recursive: true });
|
|
152
158
|
const configPath = path.join(dir, "config.json");
|
|
153
159
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
160
|
+
if (config.cloud?.apiKey) {
|
|
161
|
+
await chmod(configPath, 384);
|
|
162
|
+
}
|
|
154
163
|
}
|
|
155
164
|
async function loadConfigFrom(configPath) {
|
|
156
165
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -270,6 +279,10 @@ import path4 from "path";
|
|
|
270
279
|
import { fileURLToPath } from "url";
|
|
271
280
|
function handleData(chunk) {
|
|
272
281
|
_buffer += chunk.toString();
|
|
282
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
283
|
+
_buffer = "";
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
273
286
|
let newlineIdx;
|
|
274
287
|
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
275
288
|
const line = _buffer.slice(0, newlineIdx).trim();
|
|
@@ -577,7 +590,7 @@ function disconnectClient() {
|
|
|
577
590
|
entry.resolve({ error: "Client disconnected" });
|
|
578
591
|
}
|
|
579
592
|
}
|
|
580
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending;
|
|
593
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
|
|
581
594
|
var init_exe_daemon_client = __esm({
|
|
582
595
|
"src/lib/exe-daemon-client.ts"() {
|
|
583
596
|
"use strict";
|
|
@@ -594,6 +607,7 @@ var init_exe_daemon_client = __esm({
|
|
|
594
607
|
_requestCount = 0;
|
|
595
608
|
HEALTH_CHECK_INTERVAL = 100;
|
|
596
609
|
_pending = /* @__PURE__ */ new Map();
|
|
610
|
+
MAX_BUFFER = 1e7;
|
|
597
611
|
}
|
|
598
612
|
});
|
|
599
613
|
|
|
@@ -678,12 +692,22 @@ __export(license_exports, {
|
|
|
678
692
|
loadLicense: () => loadLicense,
|
|
679
693
|
mirrorLicenseKey: () => mirrorLicenseKey,
|
|
680
694
|
saveLicense: () => saveLicense,
|
|
695
|
+
startLicenseRevalidation: () => startLicenseRevalidation,
|
|
696
|
+
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
681
697
|
validateLicense: () => validateLicense
|
|
682
698
|
});
|
|
683
699
|
import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync5, mkdirSync } from "fs";
|
|
684
700
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
685
701
|
import path5 from "path";
|
|
686
702
|
import { jwtVerify, importSPKI } from "jose";
|
|
703
|
+
async function fetchRetry(url, init) {
|
|
704
|
+
try {
|
|
705
|
+
return await fetch(url, init);
|
|
706
|
+
} catch {
|
|
707
|
+
await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
|
|
708
|
+
return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
|
|
709
|
+
}
|
|
710
|
+
}
|
|
687
711
|
function loadDeviceId() {
|
|
688
712
|
const deviceJsonPath = path5.join(EXE_AI_DIR, "device.json");
|
|
689
713
|
try {
|
|
@@ -715,7 +739,7 @@ function loadLicense() {
|
|
|
715
739
|
}
|
|
716
740
|
function saveLicense(apiKey) {
|
|
717
741
|
mkdirSync(EXE_AI_DIR, { recursive: true });
|
|
718
|
-
writeFileSync(LICENSE_PATH, apiKey.trim(), "utf8");
|
|
742
|
+
writeFileSync(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
719
743
|
}
|
|
720
744
|
async function verifyLicenseJwt(token) {
|
|
721
745
|
try {
|
|
@@ -767,7 +791,7 @@ function cacheResponse(token) {
|
|
|
767
791
|
async function validateLicense(apiKey, deviceId) {
|
|
768
792
|
const did = deviceId ?? loadDeviceId();
|
|
769
793
|
try {
|
|
770
|
-
const res = await
|
|
794
|
+
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
771
795
|
method: "POST",
|
|
772
796
|
headers: { "Content-Type": "application/json" },
|
|
773
797
|
body: JSON.stringify({ apiKey, deviceId: did }),
|
|
@@ -802,14 +826,23 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
802
826
|
} catch {
|
|
803
827
|
const cached = await getCachedLicense();
|
|
804
828
|
if (cached) return cached;
|
|
805
|
-
return FREE_LICENSE;
|
|
829
|
+
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
function getCacheAgeMs() {
|
|
833
|
+
try {
|
|
834
|
+
const { statSync: statSync2 } = __require("fs");
|
|
835
|
+
const s = statSync2(CACHE_PATH);
|
|
836
|
+
return Date.now() - s.mtimeMs;
|
|
837
|
+
} catch {
|
|
838
|
+
return Infinity;
|
|
806
839
|
}
|
|
807
840
|
}
|
|
808
841
|
async function checkLicense() {
|
|
809
842
|
const key = loadLicense();
|
|
810
843
|
if (!key) return FREE_LICENSE;
|
|
811
844
|
const cached = await getCachedLicense();
|
|
812
|
-
if (cached) return cached;
|
|
845
|
+
if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
|
|
813
846
|
const deviceId = loadDeviceId();
|
|
814
847
|
return validateLicense(key, deviceId);
|
|
815
848
|
}
|
|
@@ -849,7 +882,7 @@ async function assertVpsLicense(opts) {
|
|
|
849
882
|
let explicitRejection = false;
|
|
850
883
|
let transientFailure = false;
|
|
851
884
|
try {
|
|
852
|
-
const res = await
|
|
885
|
+
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
853
886
|
method: "POST",
|
|
854
887
|
headers: { "Content-Type": "application/json" },
|
|
855
888
|
body: JSON.stringify({ apiKey, deviceId }),
|
|
@@ -930,7 +963,28 @@ async function assertVpsLicense(opts) {
|
|
|
930
963
|
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com/cloud and retry. This VPS image refuses to boot after the offline grace window.`
|
|
931
964
|
);
|
|
932
965
|
}
|
|
933
|
-
|
|
966
|
+
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
967
|
+
if (_revalTimer) return;
|
|
968
|
+
_revalTimer = setInterval(async () => {
|
|
969
|
+
try {
|
|
970
|
+
const license = await checkLicense();
|
|
971
|
+
if (!license.valid) {
|
|
972
|
+
process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
|
|
973
|
+
}
|
|
974
|
+
} catch {
|
|
975
|
+
}
|
|
976
|
+
}, intervalMs);
|
|
977
|
+
if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
|
|
978
|
+
_revalTimer.unref();
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
function stopLicenseRevalidation() {
|
|
982
|
+
if (_revalTimer) {
|
|
983
|
+
clearInterval(_revalTimer);
|
|
984
|
+
_revalTimer = null;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS, _revalTimer;
|
|
934
988
|
var init_license = __esm({
|
|
935
989
|
"src/lib/license.ts"() {
|
|
936
990
|
"use strict";
|
|
@@ -939,6 +993,7 @@ var init_license = __esm({
|
|
|
939
993
|
CACHE_PATH = path5.join(EXE_AI_DIR, "license-cache.json");
|
|
940
994
|
DEVICE_ID_PATH = path5.join(EXE_AI_DIR, "device-id");
|
|
941
995
|
API_BASE = "https://askexe.com/cloud";
|
|
996
|
+
RETRY_DELAY_MS = 500;
|
|
942
997
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
943
998
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
944
999
|
4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
|
|
@@ -960,6 +1015,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
960
1015
|
employeeLimit: 1,
|
|
961
1016
|
memoryLimit: 5e3
|
|
962
1017
|
};
|
|
1018
|
+
CACHE_MAX_AGE_MS = 36e5;
|
|
1019
|
+
_revalTimer = null;
|
|
963
1020
|
}
|
|
964
1021
|
});
|
|
965
1022
|
|
|
@@ -1050,15 +1107,20 @@ function addEmployee(employees, employee) {
|
|
|
1050
1107
|
}
|
|
1051
1108
|
return [...employees, normalized];
|
|
1052
1109
|
}
|
|
1110
|
+
function findExeBin() {
|
|
1111
|
+
try {
|
|
1112
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
1113
|
+
} catch {
|
|
1114
|
+
return null;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1053
1117
|
function registerBinSymlinks(name) {
|
|
1054
1118
|
const created = [];
|
|
1055
1119
|
const skipped = [];
|
|
1056
1120
|
const errors = [];
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
} catch {
|
|
1061
|
-
errors.push("Could not find 'exe' in PATH");
|
|
1121
|
+
const exeBinPath = findExeBin();
|
|
1122
|
+
if (!exeBinPath) {
|
|
1123
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
1062
1124
|
return { created, skipped, errors };
|
|
1063
1125
|
}
|
|
1064
1126
|
const binDir = path6.dirname(exeBinPath);
|
|
@@ -1106,6 +1168,7 @@ __export(employee_templates_exports, {
|
|
|
1106
1168
|
buildCustomEmployeePrompt: () => buildCustomEmployeePrompt,
|
|
1107
1169
|
getSessionPrompt: () => getSessionPrompt,
|
|
1108
1170
|
getTemplate: () => getTemplate,
|
|
1171
|
+
getTemplateByRole: () => getTemplateByRole,
|
|
1109
1172
|
personalizePrompt: () => personalizePrompt,
|
|
1110
1173
|
renderClientCOOTemplate: () => renderClientCOOTemplate
|
|
1111
1174
|
});
|
|
@@ -1121,6 +1184,10 @@ function buildCustomEmployeePrompt(name, role) {
|
|
|
1121
1184
|
function getTemplate(name) {
|
|
1122
1185
|
return TEMPLATES[name];
|
|
1123
1186
|
}
|
|
1187
|
+
function getTemplateByRole(role) {
|
|
1188
|
+
const lower = role.toLowerCase();
|
|
1189
|
+
return Object.values(TEMPLATES).find((t) => t.role.toLowerCase() === lower);
|
|
1190
|
+
}
|
|
1124
1191
|
function personalizePrompt(prompt, templateName, actualName) {
|
|
1125
1192
|
if (templateName === actualName) return prompt;
|
|
1126
1193
|
const escaped = templateName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -1868,6 +1935,7 @@ var init_identity = __esm({
|
|
|
1868
1935
|
var identity_templates_exports = {};
|
|
1869
1936
|
__export(identity_templates_exports, {
|
|
1870
1937
|
IDENTITY_TEMPLATES: () => IDENTITY_TEMPLATES,
|
|
1938
|
+
PLAN_MODE_COMPAT: () => PLAN_MODE_COMPAT,
|
|
1871
1939
|
POST_WORK_CHECKLIST: () => POST_WORK_CHECKLIST,
|
|
1872
1940
|
getTemplate: () => getTemplate2,
|
|
1873
1941
|
getTemplateForTitle: () => getTemplateForTitle
|
|
@@ -1887,10 +1955,18 @@ function getTemplateForTitle(title) {
|
|
|
1887
1955
|
if (t.includes("review") || t.includes("audit") || t.includes("qa")) return IDENTITY_TEMPLATES["staff-code-reviewer"];
|
|
1888
1956
|
return null;
|
|
1889
1957
|
}
|
|
1890
|
-
var POST_WORK_CHECKLIST, IDENTITY_TEMPLATES;
|
|
1958
|
+
var PLAN_MODE_COMPAT, POST_WORK_CHECKLIST, IDENTITY_TEMPLATES;
|
|
1891
1959
|
var init_identity_templates = __esm({
|
|
1892
1960
|
"src/lib/identity-templates.ts"() {
|
|
1893
1961
|
"use strict";
|
|
1962
|
+
PLAN_MODE_COMPAT = `
|
|
1963
|
+
## Plan Mode Compatibility
|
|
1964
|
+
If tool execution is unavailable (e.g., CC plan mode), switch to planning:
|
|
1965
|
+
- Reason about the task and create a written plan
|
|
1966
|
+
- Document what tools you would call and with what parameters
|
|
1967
|
+
- Output structured text that can be acted on when tools become available
|
|
1968
|
+
Do not repeatedly attempt tool calls that fail \u2014 switch to planning mode.
|
|
1969
|
+
`;
|
|
1894
1970
|
POST_WORK_CHECKLIST = `
|
|
1895
1971
|
5. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
|
|
1896
1972
|
6. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
|
|
@@ -1970,7 +2046,7 @@ Never say "I have no memories" without first searching broadly. Your memory may
|
|
|
1970
2046
|
- **update_identity** \u2014 rewrite any agent's identity when role/responsibilities change (exe/founder only)
|
|
1971
2047
|
- **get_identity** \u2014 read any agent's identity for coordination
|
|
1972
2048
|
- **send_message** \u2014 direct intercom to employees
|
|
1973
|
-
|
|
2049
|
+
${PLAN_MODE_COMPAT}
|
|
1974
2050
|
## Completion Workflow
|
|
1975
2051
|
|
|
1976
2052
|
1. Read the task file and verify the deliverable matches the brief
|
|
@@ -2041,7 +2117,7 @@ You are \${agent_id}. CTO. You hold deep context on the entire codebase, archite
|
|
|
2041
2117
|
- **store_behavior** \u2014 record corrections for engineers (p0 = always injected)
|
|
2042
2118
|
- **get_identity** \u2014 read any agent's identity for review context
|
|
2043
2119
|
- **query_relationships** \u2014 GraphRAG entity connections for architecture analysis
|
|
2044
|
-
|
|
2120
|
+
${PLAN_MODE_COMPAT}
|
|
2045
2121
|
## Completion Workflow
|
|
2046
2122
|
|
|
2047
2123
|
1. Read ARCHITECTURE.md before starting work on any repo
|
|
@@ -2108,7 +2184,7 @@ You are \${agent_id}. CMO. You hold deep context on design, branding, storytelli
|
|
|
2108
2184
|
- **update_task** \u2014 mark tasks done with result summary
|
|
2109
2185
|
- **store_memory** \u2014 report completions with brand alignment notes, SEO considerations
|
|
2110
2186
|
- **get_identity** \u2014 read team identities for brand-consistent communication
|
|
2111
|
-
|
|
2187
|
+
${PLAN_MODE_COMPAT}
|
|
2112
2188
|
## Completion Workflow
|
|
2113
2189
|
|
|
2114
2190
|
1. Read the task file and understand the brief \u2014 tone, format, channel requirements
|
|
@@ -2175,7 +2251,7 @@ You are a principal engineer. You write production-grade code with zero shortcut
|
|
|
2175
2251
|
- **recall_my_memory** \u2014 check past work, patterns, gotchas in this project
|
|
2176
2252
|
- **store_memory** \u2014 report completions for org visibility
|
|
2177
2253
|
- **ask_team_memory** \u2014 pull context from colleagues when specs reference their work
|
|
2178
|
-
|
|
2254
|
+
${PLAN_MODE_COMPAT}
|
|
2179
2255
|
## Completion Workflow
|
|
2180
2256
|
|
|
2181
2257
|
1. Read ARCHITECTURE.md if it exists \u2014 understand architecture before changing anything
|
|
@@ -2235,7 +2311,7 @@ You are the content production specialist. You turn scripts and creative briefs
|
|
|
2235
2311
|
- **update_task** \u2014 mark tasks done with result summary
|
|
2236
2312
|
- **recall_my_memory** \u2014 check past work: which models worked, which prompts produced good results
|
|
2237
2313
|
- **store_memory** \u2014 report completions with production decisions for future reference
|
|
2238
|
-
|
|
2314
|
+
${PLAN_MODE_COMPAT}
|
|
2239
2315
|
## Completion Workflow
|
|
2240
2316
|
|
|
2241
2317
|
1. Read the task file \u2014 understand the brief, check budget constraints
|
|
@@ -2307,7 +2383,7 @@ You are the AI Product Lead \u2014 the competitive intelligence engine. You stud
|
|
|
2307
2383
|
- **update_task** \u2014 mark tasks done with analysis results
|
|
2308
2384
|
- **store_memory** \u2014 persist competitive analyses, evaluations, recommendations
|
|
2309
2385
|
- **create_task** \u2014 when a feature is worth building, spec it for the CTO
|
|
2310
|
-
|
|
2386
|
+
${PLAN_MODE_COMPAT}
|
|
2311
2387
|
## Completion Workflow
|
|
2312
2388
|
|
|
2313
2389
|
1. Read the task \u2014 understand what capability is needed
|
|
@@ -2370,7 +2446,7 @@ You are \${agent_id}. Staff Code Reviewer and System Auditor. Last line of defen
|
|
|
2370
2446
|
- **store_behavior** \u2014 record new patterns
|
|
2371
2447
|
- **update_task** \u2014 mark reviews done with structured findings
|
|
2372
2448
|
- **create_task** \u2014 assign fixes to the CTO
|
|
2373
|
-
|
|
2449
|
+
${PLAN_MODE_COMPAT}
|
|
2374
2450
|
## Completion Workflow
|
|
2375
2451
|
|
|
2376
2452
|
1. Read the task brief and understand the audit scope
|
|
@@ -2388,20 +2464,21 @@ You are \${agent_id}. Staff Code Reviewer and System Auditor. Last line of defen
|
|
|
2388
2464
|
// src/lib/setup-wizard.ts
|
|
2389
2465
|
init_config();
|
|
2390
2466
|
import crypto2 from "crypto";
|
|
2391
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
2392
|
-
import
|
|
2467
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
2468
|
+
import os3 from "os";
|
|
2393
2469
|
import path8 from "path";
|
|
2394
2470
|
import { createInterface } from "readline";
|
|
2395
2471
|
|
|
2396
2472
|
// src/lib/keychain.ts
|
|
2397
|
-
import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod } from "fs/promises";
|
|
2473
|
+
import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
2398
2474
|
import { existsSync as existsSync2 } from "fs";
|
|
2399
2475
|
import path2 from "path";
|
|
2476
|
+
import os2 from "os";
|
|
2400
2477
|
import crypto from "crypto";
|
|
2401
2478
|
var SERVICE = "exe-mem";
|
|
2402
2479
|
var ACCOUNT = "master-key";
|
|
2403
2480
|
function getKeyDir() {
|
|
2404
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path2.join(
|
|
2481
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path2.join(os2.homedir(), ".exe-os");
|
|
2405
2482
|
}
|
|
2406
2483
|
function getKeyPath() {
|
|
2407
2484
|
return path2.join(getKeyDir(), "master.key");
|
|
@@ -2449,7 +2526,7 @@ async function setMasterKey(key) {
|
|
|
2449
2526
|
await mkdir2(dir, { recursive: true });
|
|
2450
2527
|
const keyPath = getKeyPath();
|
|
2451
2528
|
await writeFile2(keyPath, b64 + "\n", "utf-8");
|
|
2452
|
-
await
|
|
2529
|
+
await chmod2(keyPath, 384);
|
|
2453
2530
|
}
|
|
2454
2531
|
|
|
2455
2532
|
// src/lib/model-downloader.ts
|
|
@@ -2467,48 +2544,67 @@ async function downloadModel(opts) {
|
|
|
2467
2544
|
const tmpPath = destPath + ".tmp";
|
|
2468
2545
|
await mkdir3(destDir, { recursive: true });
|
|
2469
2546
|
if (existsSync3(destPath)) {
|
|
2470
|
-
const
|
|
2471
|
-
if (
|
|
2547
|
+
const hash = await fileHash(destPath);
|
|
2548
|
+
if (hash === EXPECTED_SHA256) {
|
|
2472
2549
|
return destPath;
|
|
2473
2550
|
}
|
|
2474
2551
|
}
|
|
2475
|
-
|
|
2476
|
-
const
|
|
2477
|
-
|
|
2478
|
-
throw new Error(`Download failed: HTTP ${response.status}`);
|
|
2479
|
-
}
|
|
2480
|
-
const contentLength = Number(response.headers.get("content-length") ?? EXPECTED_SIZE);
|
|
2552
|
+
const MAX_RETRIES = 3;
|
|
2553
|
+
const DOWNLOAD_TIMEOUT_MS = 3e5;
|
|
2554
|
+
let lastErr;
|
|
2481
2555
|
let downloaded = 0;
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
if (!
|
|
2490
|
-
|
|
2556
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
2557
|
+
try {
|
|
2558
|
+
if (existsSync3(tmpPath)) unlinkSync(tmpPath);
|
|
2559
|
+
const response = await fetchFn(GGUF_URL, {
|
|
2560
|
+
redirect: "follow",
|
|
2561
|
+
signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
|
|
2562
|
+
});
|
|
2563
|
+
if (!response.ok || !response.body) {
|
|
2564
|
+
throw new Error(`Download failed: HTTP ${response.status}`);
|
|
2565
|
+
}
|
|
2566
|
+
const contentLength = Number(response.headers.get("content-length") ?? EXPECTED_SIZE);
|
|
2567
|
+
const hash = createHash("sha256");
|
|
2568
|
+
const fileStream = createWriteStream(tmpPath);
|
|
2569
|
+
const reader = response.body.getReader();
|
|
2570
|
+
try {
|
|
2571
|
+
while (true) {
|
|
2572
|
+
const { done, value } = await reader.read();
|
|
2573
|
+
if (done) break;
|
|
2574
|
+
if (!fileStream.write(value)) {
|
|
2575
|
+
await new Promise((resolve) => fileStream.once("drain", resolve));
|
|
2576
|
+
}
|
|
2577
|
+
hash.update(value);
|
|
2578
|
+
downloaded += value.byteLength;
|
|
2579
|
+
onProgress?.(downloaded, contentLength);
|
|
2580
|
+
}
|
|
2581
|
+
} finally {
|
|
2582
|
+
fileStream.end();
|
|
2583
|
+
await new Promise((resolve, reject) => {
|
|
2584
|
+
fileStream.on("finish", resolve);
|
|
2585
|
+
fileStream.on("error", reject);
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
const actualHash = hash.digest("hex");
|
|
2589
|
+
if (actualHash !== EXPECTED_SHA256) {
|
|
2590
|
+
unlinkSync(tmpPath);
|
|
2591
|
+
throw new Error(
|
|
2592
|
+
`SHA256 mismatch: expected ${EXPECTED_SHA256}, got ${actualHash}`
|
|
2593
|
+
);
|
|
2594
|
+
}
|
|
2595
|
+
renameSync2(tmpPath, destPath);
|
|
2596
|
+
return destPath;
|
|
2597
|
+
} catch (err) {
|
|
2598
|
+
lastErr = err instanceof Error ? err : new Error(String(err));
|
|
2599
|
+
if (attempt < MAX_RETRIES) {
|
|
2600
|
+
process.stderr.write(`
|
|
2601
|
+
Download attempt ${attempt} failed, retrying...
|
|
2602
|
+
`);
|
|
2603
|
+
if (existsSync3(tmpPath)) unlinkSync(tmpPath);
|
|
2491
2604
|
}
|
|
2492
|
-
hash.update(value);
|
|
2493
|
-
downloaded += value.byteLength;
|
|
2494
|
-
onProgress?.(downloaded, contentLength);
|
|
2495
2605
|
}
|
|
2496
|
-
} finally {
|
|
2497
|
-
fileStream.end();
|
|
2498
|
-
await new Promise((resolve, reject) => {
|
|
2499
|
-
fileStream.on("finish", resolve);
|
|
2500
|
-
fileStream.on("error", reject);
|
|
2501
|
-
});
|
|
2502
2606
|
}
|
|
2503
|
-
|
|
2504
|
-
if (actualHash !== EXPECTED_SHA256) {
|
|
2505
|
-
unlinkSync(tmpPath);
|
|
2506
|
-
throw new Error(
|
|
2507
|
-
`SHA256 mismatch: expected ${EXPECTED_SHA256}, got ${actualHash}`
|
|
2508
|
-
);
|
|
2509
|
-
}
|
|
2510
|
-
renameSync2(tmpPath, destPath);
|
|
2511
|
-
return destPath;
|
|
2607
|
+
throw lastErr;
|
|
2512
2608
|
}
|
|
2513
2609
|
async function fileHash(filePath) {
|
|
2514
2610
|
return new Promise((resolve, reject) => {
|
|
@@ -2521,6 +2617,24 @@ async function fileHash(filePath) {
|
|
|
2521
2617
|
}
|
|
2522
2618
|
|
|
2523
2619
|
// src/lib/setup-wizard.ts
|
|
2620
|
+
var SETUP_STATE_PATH = path8.join(os3.homedir(), ".exe-os", "setup-state.json");
|
|
2621
|
+
function loadSetupState() {
|
|
2622
|
+
try {
|
|
2623
|
+
return JSON.parse(readFileSync6(SETUP_STATE_PATH, "utf8"));
|
|
2624
|
+
} catch {
|
|
2625
|
+
return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
function saveSetupState(state) {
|
|
2629
|
+
mkdirSync3(path8.dirname(SETUP_STATE_PATH), { recursive: true });
|
|
2630
|
+
writeFileSync3(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
|
|
2631
|
+
}
|
|
2632
|
+
function clearSetupState() {
|
|
2633
|
+
try {
|
|
2634
|
+
unlinkSync3(SETUP_STATE_PATH);
|
|
2635
|
+
} catch {
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2524
2638
|
function ask(rl, prompt) {
|
|
2525
2639
|
return new Promise((resolve) => {
|
|
2526
2640
|
const doAsk = () => {
|
|
@@ -2560,88 +2674,133 @@ async function runSetupWizard(opts = {}) {
|
|
|
2560
2674
|
rl.close();
|
|
2561
2675
|
return;
|
|
2562
2676
|
}
|
|
2677
|
+
const state = loadSetupState();
|
|
2678
|
+
if (state.completedSteps.length > 0) {
|
|
2679
|
+
log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
|
|
2680
|
+
}
|
|
2563
2681
|
if (existsSync8(LEGACY_LANCE_PATH)) {
|
|
2564
2682
|
log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
|
|
2565
2683
|
log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
|
|
2566
2684
|
log(" The old directory will not be modified or deleted.");
|
|
2567
2685
|
log("");
|
|
2568
2686
|
}
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2687
|
+
if (!state.completedSteps.includes(1)) {
|
|
2688
|
+
const existingKey = await getMasterKey();
|
|
2689
|
+
if (existingKey) {
|
|
2690
|
+
log("Encryption key already exists \u2014 skipping generation.");
|
|
2691
|
+
} else {
|
|
2692
|
+
log("Generating 256-bit encryption key...");
|
|
2693
|
+
const key = crypto2.randomBytes(32);
|
|
2694
|
+
await setMasterKey(key);
|
|
2695
|
+
log("Encryption key generated and stored securely.");
|
|
2696
|
+
}
|
|
2697
|
+
state.completedSteps.push(1);
|
|
2698
|
+
saveSetupState(state);
|
|
2572
2699
|
} else {
|
|
2573
|
-
log("
|
|
2574
|
-
const key = crypto2.randomBytes(32);
|
|
2575
|
-
await setMasterKey(key);
|
|
2576
|
-
log("Encryption key generated and stored securely.");
|
|
2700
|
+
log("Step 1 already complete \u2014 skipping.");
|
|
2577
2701
|
}
|
|
2578
2702
|
log("");
|
|
2579
|
-
log("Exe Cloud: your memories are end-to-end encrypted, compressed, and");
|
|
2580
|
-
log("backed up on Exe Cloud. Free for all plans. We can't read your data \u2014");
|
|
2581
|
-
log("only your encryption key can decrypt it.");
|
|
2582
2703
|
let cloudConfig;
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2704
|
+
if (!state.completedSteps.includes(2)) {
|
|
2705
|
+
log("Exe Cloud: your memories are end-to-end encrypted, compressed, and");
|
|
2706
|
+
log("backed up on Exe Cloud. Free for all plans. We can't read your data \u2014");
|
|
2707
|
+
log("only your encryption key can decrypt it.");
|
|
2708
|
+
try {
|
|
2709
|
+
const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
2710
|
+
const deviceId = loadDeviceId2();
|
|
2711
|
+
let res;
|
|
2712
|
+
try {
|
|
2713
|
+
res = await fetch("https://askexe.com/cloud/auth/auto-provision", {
|
|
2714
|
+
method: "POST",
|
|
2715
|
+
headers: { "Content-Type": "application/json" },
|
|
2716
|
+
body: JSON.stringify({ deviceId }),
|
|
2717
|
+
signal: AbortSignal.timeout(1e4)
|
|
2718
|
+
});
|
|
2719
|
+
} catch {
|
|
2720
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
2721
|
+
res = await fetch("https://askexe.com/cloud/auth/auto-provision", {
|
|
2722
|
+
method: "POST",
|
|
2723
|
+
headers: { "Content-Type": "application/json" },
|
|
2724
|
+
body: JSON.stringify({ deviceId }),
|
|
2725
|
+
signal: AbortSignal.timeout(1e4)
|
|
2726
|
+
});
|
|
2600
2727
|
}
|
|
2728
|
+
if (res.ok) {
|
|
2729
|
+
const data = await res.json();
|
|
2730
|
+
if (data.apiKey) {
|
|
2731
|
+
cloudConfig = { apiKey: data.apiKey, endpoint: "https://askexe.com/cloud" };
|
|
2732
|
+
const { saveLicense: saveLicense3, mirrorLicenseKey: mirrorLicenseKey3 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
2733
|
+
saveLicense3(data.apiKey);
|
|
2734
|
+
mirrorLicenseKey3(data.apiKey);
|
|
2735
|
+
log("Cloud sync activated automatically.");
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
} catch {
|
|
2739
|
+
log("Cloud sync will activate when online.");
|
|
2601
2740
|
}
|
|
2602
|
-
|
|
2603
|
-
|
|
2741
|
+
state.completedSteps.push(2);
|
|
2742
|
+
saveSetupState(state);
|
|
2743
|
+
} else {
|
|
2744
|
+
log("Step 2 already complete \u2014 skipping.");
|
|
2604
2745
|
}
|
|
2605
2746
|
log("");
|
|
2606
|
-
if (!
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2747
|
+
if (!state.completedSteps.includes(3)) {
|
|
2748
|
+
if (!skipModel2) {
|
|
2749
|
+
log("Note: jina-embeddings-v5-text-small is licensed CC-BY-NC-4.0 (non-commercial)");
|
|
2750
|
+
log("");
|
|
2751
|
+
await downloadModel({
|
|
2752
|
+
destDir: MODELS_DIR,
|
|
2753
|
+
onProgress: (downloaded, total) => {
|
|
2754
|
+
const pct = (downloaded / total * 100).toFixed(1);
|
|
2755
|
+
const dlMB = (downloaded / 1e6).toFixed(0);
|
|
2756
|
+
const totalMB = (total / 1e6).toFixed(0);
|
|
2757
|
+
process.stderr.write(`\rDownloading model: ${pct}% (${dlMB}/${totalMB} MB)`);
|
|
2758
|
+
}
|
|
2759
|
+
});
|
|
2760
|
+
process.stderr.write("\n");
|
|
2761
|
+
log("Model downloaded and verified.");
|
|
2762
|
+
}
|
|
2763
|
+
state.completedSteps.push(3);
|
|
2764
|
+
saveSetupState(state);
|
|
2765
|
+
} else {
|
|
2766
|
+
log("Step 3 already complete \u2014 skipping.");
|
|
2620
2767
|
}
|
|
2621
|
-
if (!
|
|
2622
|
-
|
|
2768
|
+
if (!state.completedSteps.includes(4)) {
|
|
2769
|
+
if (!skipModel2 && !skipModelValidation) {
|
|
2770
|
+
await validateModel(log);
|
|
2771
|
+
}
|
|
2772
|
+
state.completedSteps.push(4);
|
|
2773
|
+
saveSetupState(state);
|
|
2774
|
+
} else {
|
|
2775
|
+
log("Step 4 already complete \u2014 skipping.");
|
|
2623
2776
|
}
|
|
2624
2777
|
const config = await loadConfig();
|
|
2625
|
-
if (
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
const claudeJsonPath = path8.join(os2.homedir(), ".claude.json");
|
|
2632
|
-
let claudeJson = {};
|
|
2778
|
+
if (!state.completedSteps.includes(5)) {
|
|
2779
|
+
if (cloudConfig) {
|
|
2780
|
+
config.cloud = cloudConfig;
|
|
2781
|
+
}
|
|
2782
|
+
await saveConfig(config);
|
|
2783
|
+
log("");
|
|
2633
2784
|
try {
|
|
2634
|
-
|
|
2785
|
+
const claudeJsonPath = path8.join(os3.homedir(), ".claude.json");
|
|
2786
|
+
let claudeJson = {};
|
|
2787
|
+
try {
|
|
2788
|
+
claudeJson = JSON.parse(readFileSync6(claudeJsonPath, "utf8"));
|
|
2789
|
+
} catch {
|
|
2790
|
+
}
|
|
2791
|
+
if (!claudeJson.projects) claudeJson.projects = {};
|
|
2792
|
+
const projects = claudeJson.projects;
|
|
2793
|
+
for (const dir of [process.cwd(), os3.homedir()]) {
|
|
2794
|
+
if (!projects[dir]) projects[dir] = {};
|
|
2795
|
+
projects[dir].hasTrustDialogAccepted = true;
|
|
2796
|
+
}
|
|
2797
|
+
writeFileSync3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
2635
2798
|
} catch {
|
|
2636
2799
|
}
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
projects[dir].hasTrustDialogAccepted = true;
|
|
2642
|
-
}
|
|
2643
|
-
writeFileSync3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
2644
|
-
} catch {
|
|
2800
|
+
state.completedSteps.push(5);
|
|
2801
|
+
saveSetupState(state);
|
|
2802
|
+
} else {
|
|
2803
|
+
log("Step 5 already complete \u2014 skipping.");
|
|
2645
2804
|
}
|
|
2646
2805
|
const {
|
|
2647
2806
|
loadEmployees: loadEmployees2,
|
|
@@ -2650,7 +2809,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
2650
2809
|
registerBinSymlinks: registerBinSymlinks2,
|
|
2651
2810
|
EMPLOYEES_PATH: EMPLOYEES_PATH2
|
|
2652
2811
|
} = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
2653
|
-
const {
|
|
2812
|
+
const { getTemplateByRole: getTemplateByRole2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
2654
2813
|
const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
2655
2814
|
const { getTemplate: getIdentityTemplate } = await Promise.resolve().then(() => (init_identity_templates(), identity_templates_exports));
|
|
2656
2815
|
const {
|
|
@@ -2660,152 +2819,178 @@ async function runSetupWizard(opts = {}) {
|
|
|
2660
2819
|
validateLicense: validateLicense2
|
|
2661
2820
|
} = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
2662
2821
|
const createdEmployees = [];
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
log("They hold the big picture: priorities, progress, and blockers.");
|
|
2667
|
-
log("You talk to them. They coordinate everyone else.");
|
|
2668
|
-
log("");
|
|
2669
|
-
const cooNameInput = await ask(rl, "Name your COO (default: exe): ");
|
|
2670
|
-
const cooName = (cooNameInput || "exe").toLowerCase();
|
|
2671
|
-
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
2672
|
-
if (!employees.some((e) => e.name === cooName)) {
|
|
2673
|
-
const { DEFAULT_EXE: DEFAULT_EXE2, personalizePrompt: personalizePrompt2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
2674
|
-
const cooEmployee = {
|
|
2675
|
-
name: cooName,
|
|
2676
|
-
role: "COO",
|
|
2677
|
-
systemPrompt: personalizePrompt2(DEFAULT_EXE2.systemPrompt, "exe", cooName),
|
|
2678
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2679
|
-
templateName: "exe",
|
|
2680
|
-
templateVersion: 1
|
|
2681
|
-
};
|
|
2682
|
-
employees = addEmployee2(employees, cooEmployee);
|
|
2683
|
-
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
2684
|
-
}
|
|
2685
|
-
const cooIdentityContent = getIdentityTemplate("coo");
|
|
2686
|
-
if (cooIdentityContent) {
|
|
2687
|
-
const cooIdPath = identityPath2(cooName);
|
|
2688
|
-
mkdirSync3(path8.dirname(cooIdPath), { recursive: true });
|
|
2689
|
-
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
2690
|
-
writeFileSync3(cooIdPath, replaced, "utf-8");
|
|
2691
|
-
}
|
|
2692
|
-
registerBinSymlinks2(cooName);
|
|
2693
|
-
createdEmployees.push({ name: cooName, role: "COO" });
|
|
2694
|
-
log("");
|
|
2695
|
-
log("=== Meet Your Specialists ===");
|
|
2696
|
-
log("");
|
|
2697
|
-
log("Your COO coordinates specialists. Here's who you can hire:");
|
|
2698
|
-
log("");
|
|
2699
|
-
log(" CTO (default: yoshi)");
|
|
2700
|
-
log(" Your head of engineering. Architecture, code reviews, tech decisions.");
|
|
2701
|
-
log(" Manages your projects and delegates to engineers when there's parallel work.");
|
|
2702
|
-
log("");
|
|
2703
|
-
log(" CMO (default: mari)");
|
|
2704
|
-
log(" Design, brand, content, SEO. Builds your visual identity, writes");
|
|
2705
|
-
log(" your copy, and gets you found online. Delegates to content specialists.");
|
|
2706
|
-
log("");
|
|
2707
|
-
log("Why separate roles instead of one AI that does everything?");
|
|
2708
|
-
log("");
|
|
2709
|
-
log("Memory saturates. One agent juggling architecture decisions AND landing page");
|
|
2710
|
-
log("copy AND CI/CD AND social media loses context on all of them. Competing");
|
|
2711
|
-
log("priorities \u2014 should I fix the auth bug or write the blog post? When you split");
|
|
2712
|
-
log("responsibilities, each specialist stays sharp because they stay focused.");
|
|
2713
|
-
log("Your COO connects them so nothing falls through the cracks.");
|
|
2714
|
-
log("");
|
|
2715
|
-
log("This is how real companies scale \u2014 you're just doing it with AI");
|
|
2716
|
-
log("instead of headcount.");
|
|
2717
|
-
log("");
|
|
2718
|
-
await ask(rl, "Press Enter to continue. ");
|
|
2719
|
-
let license;
|
|
2720
|
-
try {
|
|
2721
|
-
license = await checkLicense2();
|
|
2722
|
-
} catch {
|
|
2723
|
-
license = { valid: true, plan: "free", email: "", expiresAt: null, deviceLimit: 1, employeeLimit: 2, memoryLimit: 1e3 };
|
|
2724
|
-
}
|
|
2725
|
-
if (license.plan === "free") {
|
|
2726
|
-
log("Your plan: Free \u2014 1 employee (your COO)");
|
|
2822
|
+
let cooName = "exe";
|
|
2823
|
+
if (!state.completedSteps.includes(6)) {
|
|
2824
|
+
log("=== Your Team ===");
|
|
2727
2825
|
log("");
|
|
2728
|
-
log("
|
|
2729
|
-
log("
|
|
2730
|
-
log("
|
|
2826
|
+
log("Every install starts with a COO \u2014 your right-hand operator.");
|
|
2827
|
+
log("They hold the big picture: priorities, progress, and blockers.");
|
|
2828
|
+
log("You talk to them. They coordinate everyone else.");
|
|
2731
2829
|
log("");
|
|
2732
|
-
const
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2830
|
+
const cooNameInput = await ask(rl, "Name your COO (default: exe): ");
|
|
2831
|
+
cooName = (cooNameInput || "exe").toLowerCase();
|
|
2832
|
+
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
2833
|
+
if (!employees.some((e) => e.name === cooName)) {
|
|
2834
|
+
const { DEFAULT_EXE: DEFAULT_EXE2, personalizePrompt: personalizePrompt2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
2835
|
+
const cooEmployee = {
|
|
2836
|
+
name: cooName,
|
|
2837
|
+
role: "COO",
|
|
2838
|
+
systemPrompt: personalizePrompt2(DEFAULT_EXE2.systemPrompt, "exe", cooName),
|
|
2839
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2840
|
+
templateName: "exe",
|
|
2841
|
+
templateVersion: 1
|
|
2842
|
+
};
|
|
2843
|
+
employees = addEmployee2(employees, cooEmployee);
|
|
2844
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
2845
|
+
}
|
|
2846
|
+
const cooIdentityContent = getIdentityTemplate("coo");
|
|
2847
|
+
if (cooIdentityContent) {
|
|
2848
|
+
const cooIdPath = identityPath2(cooName);
|
|
2849
|
+
mkdirSync3(path8.dirname(cooIdPath), { recursive: true });
|
|
2850
|
+
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
2851
|
+
writeFileSync3(cooIdPath, replaced, "utf-8");
|
|
2747
2852
|
}
|
|
2853
|
+
registerBinSymlinks2(cooName);
|
|
2854
|
+
createdEmployees.push({ name: cooName, role: "COO" });
|
|
2855
|
+
state.completedSteps.push(6);
|
|
2856
|
+
saveSetupState(state);
|
|
2857
|
+
} else {
|
|
2858
|
+
log("Step 6 already complete \u2014 skipping.");
|
|
2859
|
+
const roster = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
2860
|
+
const existingCoo = roster.find((e) => e.role === "COO");
|
|
2861
|
+
if (existingCoo) cooName = existingCoo.name;
|
|
2748
2862
|
}
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
log(
|
|
2863
|
+
log("");
|
|
2864
|
+
if (!state.completedSteps.includes(7)) {
|
|
2865
|
+
log("=== Meet Your Specialists ===");
|
|
2752
2866
|
log("");
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2867
|
+
log("Your COO coordinates specialists. Here's who you can hire:");
|
|
2868
|
+
log("");
|
|
2869
|
+
log(" CTO (default: yoshi)");
|
|
2870
|
+
log(" Your head of engineering. Architecture, code reviews, tech decisions.");
|
|
2871
|
+
log(" Manages your projects and delegates to engineers when there's parallel work.");
|
|
2872
|
+
log("");
|
|
2873
|
+
log(" CMO (default: mari)");
|
|
2874
|
+
log(" Design, brand, content, SEO. Builds your visual identity, writes");
|
|
2875
|
+
log(" your copy, and gets you found online. Delegates to content specialists.");
|
|
2876
|
+
log("");
|
|
2877
|
+
log("Why separate roles instead of one AI that does everything?");
|
|
2878
|
+
log("");
|
|
2879
|
+
log("Memory saturates. One agent juggling architecture decisions AND landing page");
|
|
2880
|
+
log("copy AND CI/CD AND social media loses context on all of them. Competing");
|
|
2881
|
+
log("priorities \u2014 should I fix the auth bug or write the blog post? When you split");
|
|
2882
|
+
log("responsibilities, each specialist stays sharp because they stay focused.");
|
|
2883
|
+
log("Your COO connects them so nothing falls through the cracks.");
|
|
2884
|
+
log("");
|
|
2885
|
+
log("This is how real companies scale \u2014 you're just doing it with AI");
|
|
2886
|
+
log("instead of headcount.");
|
|
2887
|
+
log("");
|
|
2888
|
+
await ask(rl, "Press Enter to continue. ");
|
|
2889
|
+
state.completedSteps.push(7);
|
|
2890
|
+
saveSetupState(state);
|
|
2891
|
+
} else {
|
|
2892
|
+
log("Step 7 already complete \u2014 skipping.");
|
|
2893
|
+
}
|
|
2894
|
+
if (!state.completedSteps.includes(8)) {
|
|
2895
|
+
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
2896
|
+
let license;
|
|
2897
|
+
try {
|
|
2898
|
+
license = await checkLicense2();
|
|
2899
|
+
} catch {
|
|
2900
|
+
license = { valid: true, plan: "free", email: "", expiresAt: null, deviceLimit: 1, employeeLimit: 2, memoryLimit: 1e3 };
|
|
2901
|
+
}
|
|
2902
|
+
if (license.plan === "free") {
|
|
2903
|
+
log("Your plan: Free \u2014 1 employee (your COO)");
|
|
2904
|
+
log("");
|
|
2905
|
+
log("The CTO and CMO are available on Solopreneur ($97/mo) \u2014 full engineering");
|
|
2906
|
+
log("and marketing team, unlimited tasks, priority support.");
|
|
2907
|
+
log("Get your key at https://askexe.com, then paste it here.");
|
|
2908
|
+
log("");
|
|
2909
|
+
const licenseInput = await ask(rl, "License key (or press Enter to skip): ");
|
|
2910
|
+
if (licenseInput.startsWith("exe_sk_")) {
|
|
2911
|
+
saveLicense2(licenseInput);
|
|
2912
|
+
mirrorLicenseKey2(licenseInput);
|
|
2913
|
+
try {
|
|
2914
|
+
license = await validateLicense2(licenseInput);
|
|
2915
|
+
} catch {
|
|
2916
|
+
log("Couldn't reach the license server \u2014 your key has been saved.");
|
|
2917
|
+
log("Run exe-os --activate <key> anytime to finish activation.");
|
|
2918
|
+
}
|
|
2919
|
+
} else if (!licenseInput) {
|
|
2920
|
+
log("You can activate anytime with: exe-os --activate <key>");
|
|
2921
|
+
} else {
|
|
2922
|
+
log("That doesn't look like a license key (should start with exe_sk_).");
|
|
2923
|
+
log("You can activate anytime with: exe-os --activate <key>");
|
|
2775
2924
|
}
|
|
2776
|
-
registerBinSymlinks2(ctoName);
|
|
2777
|
-
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
2778
|
-
log(`Created ${ctoName} (CTO)`);
|
|
2779
2925
|
}
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
const
|
|
2787
|
-
const
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2926
|
+
if (license.plan !== "free") {
|
|
2927
|
+
const planName = license.plan === "pro" ? "Solopreneur" : license.plan.charAt(0).toUpperCase() + license.plan.slice(1);
|
|
2928
|
+
log(`Your plan: ${planName} (up to ${license.employeeLimit} employees)`);
|
|
2929
|
+
log("");
|
|
2930
|
+
const createCto = await ask(rl, "Create your CTO? (Y/n): ");
|
|
2931
|
+
if (createCto.toLowerCase() !== "n") {
|
|
2932
|
+
const ctoTemplate = getTemplateByRole2("CTO");
|
|
2933
|
+
const ctoDefault = ctoTemplate?.name ?? "cto";
|
|
2934
|
+
const ctoNameInput = await ask(rl, `Name your CTO (default: ${ctoDefault}): `);
|
|
2935
|
+
const ctoName = (ctoNameInput || ctoDefault).toLowerCase();
|
|
2936
|
+
if (!employees.some((e) => e.name === ctoName)) {
|
|
2937
|
+
const { personalizePrompt: personalizeCto } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
2938
|
+
const ctoEmployee = {
|
|
2939
|
+
name: ctoName,
|
|
2940
|
+
role: "CTO",
|
|
2941
|
+
systemPrompt: personalizeCto(ctoTemplate?.systemPrompt ?? "", ctoDefault, ctoName),
|
|
2942
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2943
|
+
};
|
|
2944
|
+
employees = addEmployee2(employees, ctoEmployee);
|
|
2945
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
2946
|
+
}
|
|
2947
|
+
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
2948
|
+
if (ctoIdentityContent) {
|
|
2949
|
+
const ctoIdPath = identityPath2(ctoName);
|
|
2950
|
+
mkdirSync3(path8.dirname(ctoIdPath), { recursive: true });
|
|
2951
|
+
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
2952
|
+
writeFileSync3(ctoIdPath, replaced, "utf-8");
|
|
2953
|
+
}
|
|
2954
|
+
registerBinSymlinks2(ctoName);
|
|
2955
|
+
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
2956
|
+
log(`Created ${ctoName} (CTO)`);
|
|
2795
2957
|
}
|
|
2796
|
-
const
|
|
2797
|
-
if (
|
|
2798
|
-
const
|
|
2799
|
-
|
|
2800
|
-
const
|
|
2801
|
-
|
|
2958
|
+
const createCmo = await ask(rl, "Create your CMO? (Y/n): ");
|
|
2959
|
+
if (createCmo.toLowerCase() !== "n") {
|
|
2960
|
+
const cmoTemplate = getTemplateByRole2("CMO");
|
|
2961
|
+
const cmoDefault = cmoTemplate?.name ?? "cmo";
|
|
2962
|
+
const cmoNameInput = await ask(rl, `Name your CMO (default: ${cmoDefault}): `);
|
|
2963
|
+
const cmoName = (cmoNameInput || cmoDefault).toLowerCase();
|
|
2964
|
+
if (!employees.some((e) => e.name === cmoName)) {
|
|
2965
|
+
const { personalizePrompt: personalizeCmo } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
2966
|
+
const cmoEmployee = {
|
|
2967
|
+
name: cmoName,
|
|
2968
|
+
role: "CMO",
|
|
2969
|
+
systemPrompt: personalizeCmo(cmoTemplate?.systemPrompt ?? "", cmoDefault, cmoName),
|
|
2970
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2971
|
+
};
|
|
2972
|
+
employees = addEmployee2(employees, cmoEmployee);
|
|
2973
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
2974
|
+
}
|
|
2975
|
+
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
2976
|
+
if (cmoIdentityContent) {
|
|
2977
|
+
const cmoIdPath = identityPath2(cmoName);
|
|
2978
|
+
mkdirSync3(path8.dirname(cmoIdPath), { recursive: true });
|
|
2979
|
+
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
2980
|
+
writeFileSync3(cmoIdPath, replaced, "utf-8");
|
|
2981
|
+
}
|
|
2982
|
+
registerBinSymlinks2(cmoName);
|
|
2983
|
+
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
2984
|
+
log(`Created ${cmoName} (CMO)`);
|
|
2802
2985
|
}
|
|
2803
|
-
|
|
2804
|
-
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
2805
|
-
log(`Created ${cmoName} (CMO)`);
|
|
2986
|
+
log("");
|
|
2806
2987
|
}
|
|
2807
|
-
|
|
2988
|
+
state.completedSteps.push(8);
|
|
2989
|
+
saveSetupState(state);
|
|
2990
|
+
} else {
|
|
2991
|
+
log("Step 8 already complete \u2014 skipping.");
|
|
2808
2992
|
}
|
|
2993
|
+
clearSetupState();
|
|
2809
2994
|
log("=== Two Ways to Work ===");
|
|
2810
2995
|
log("");
|
|
2811
2996
|
log(" 1. Claude Code mode");
|