squadrant 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -1
- package/dist/index.js +758 -334
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +471 -69
- package/dist/squadrantd.js.map +1 -1
- package/package.json +1 -1
- package/plugin/skills/telegram/SKILL.md +94 -0
package/dist/index.js
CHANGED
|
@@ -107,6 +107,66 @@ var init_config = __esm({
|
|
|
107
107
|
}
|
|
108
108
|
});
|
|
109
109
|
|
|
110
|
+
// packages/shared/dist/project-config.js
|
|
111
|
+
import fs2 from "fs";
|
|
112
|
+
import os2 from "os";
|
|
113
|
+
import path2 from "path";
|
|
114
|
+
function defaultRoot() {
|
|
115
|
+
return path2.join(os2.homedir(), ".config", "squadrant");
|
|
116
|
+
}
|
|
117
|
+
function projectConfigPath(name, root = defaultRoot()) {
|
|
118
|
+
return path2.join(root, "projects", `${name}.json`);
|
|
119
|
+
}
|
|
120
|
+
function loadProjectOverride(name, root = defaultRoot()) {
|
|
121
|
+
try {
|
|
122
|
+
return JSON.parse(fs2.readFileSync(projectConfigPath(name, root), "utf-8"));
|
|
123
|
+
} catch {
|
|
124
|
+
return {};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function deepMerge(base, patch) {
|
|
128
|
+
if (patch === null || typeof patch !== "object" || Array.isArray(patch))
|
|
129
|
+
return patch ?? base;
|
|
130
|
+
const out = { ...base };
|
|
131
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
132
|
+
out[k] = deepMerge(out[k], v);
|
|
133
|
+
}
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
function saveProjectOverride(name, patch, root = defaultRoot()) {
|
|
137
|
+
const merged = deepMerge(loadProjectOverride(name, root), patch);
|
|
138
|
+
const file = projectConfigPath(name, root);
|
|
139
|
+
fs2.mkdirSync(path2.dirname(file), { recursive: true });
|
|
140
|
+
fs2.writeFileSync(file, JSON.stringify(merged, null, 2) + "\n");
|
|
141
|
+
}
|
|
142
|
+
function crewRank(tier) {
|
|
143
|
+
return CREW_RANK[tier];
|
|
144
|
+
}
|
|
145
|
+
function isQuieter(before, after) {
|
|
146
|
+
if (before.active && !after.active)
|
|
147
|
+
return { quieter: true, dim: "active" };
|
|
148
|
+
if (before.cap && !after.cap)
|
|
149
|
+
return { quieter: true, dim: "cap" };
|
|
150
|
+
if (crewRank(after.crew) < crewRank(before.crew))
|
|
151
|
+
return { quieter: true, dim: "crew" };
|
|
152
|
+
return { quieter: false, dim: null };
|
|
153
|
+
}
|
|
154
|
+
function resolveNotify(globalNotify, override) {
|
|
155
|
+
let n = { ...DEFAULT_NOTIFY };
|
|
156
|
+
if (globalNotify)
|
|
157
|
+
n = deepMerge(n, globalNotify);
|
|
158
|
+
if (override.telegram?.notify)
|
|
159
|
+
n = deepMerge(n, override.telegram.notify);
|
|
160
|
+
return n;
|
|
161
|
+
}
|
|
162
|
+
var DEFAULT_NOTIFY, CREW_RANK;
|
|
163
|
+
var init_project_config = __esm({
|
|
164
|
+
"packages/shared/dist/project-config.js"() {
|
|
165
|
+
DEFAULT_NOTIFY = { active: false, cap: true, crew: "alert_only" };
|
|
166
|
+
CREW_RANK = { none: 0, done_only: 1, alert_only: 2, all: 3 };
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
110
170
|
// packages/shared/dist/effort.js
|
|
111
171
|
function resolveEffort(config) {
|
|
112
172
|
return config.defaults.effort ?? "balance";
|
|
@@ -155,22 +215,22 @@ function defaultCmuxConfigPath() {
|
|
|
155
215
|
return join(homedir(), ".config", "cmux", "cmux.json");
|
|
156
216
|
}
|
|
157
217
|
function ensureSocketAutomation(opts = {}) {
|
|
158
|
-
const
|
|
159
|
-
if (!existsSync(
|
|
160
|
-
mkdirSync(dirname(
|
|
161
|
-
writeFileSync(
|
|
162
|
-
return { path:
|
|
218
|
+
const path29 = opts.path ?? defaultCmuxConfigPath();
|
|
219
|
+
if (!existsSync(path29)) {
|
|
220
|
+
mkdirSync(dirname(path29), { recursive: true });
|
|
221
|
+
writeFileSync(path29, MINIMAL_TEMPLATE);
|
|
222
|
+
return { path: path29, changed: true, alreadySet: false };
|
|
163
223
|
}
|
|
164
|
-
const text = readFileSync(
|
|
224
|
+
const text = readFileSync(path29, "utf-8");
|
|
165
225
|
const current = parse(text)?.automation?.socketControlMode;
|
|
166
226
|
if (current === AUTOMATION_MODE) {
|
|
167
|
-
return { path:
|
|
227
|
+
return { path: path29, changed: false, alreadySet: true };
|
|
168
228
|
}
|
|
169
229
|
const edits = modify(text, [...SOCKET_CONTROL_MODE_PATH], AUTOMATION_MODE, {
|
|
170
230
|
formattingOptions: { insertSpaces: true, tabSize: 2 }
|
|
171
231
|
});
|
|
172
|
-
writeFileSync(
|
|
173
|
-
return { path:
|
|
232
|
+
writeFileSync(path29, applyEdits(text, edits));
|
|
233
|
+
return { path: path29, changed: true, alreadySet: false };
|
|
174
234
|
}
|
|
175
235
|
var SOCKET_CONTROL_MODE_PATH, AUTOMATION_MODE, MINIMAL_TEMPLATE;
|
|
176
236
|
var init_cmux_config = __esm({
|
|
@@ -320,9 +380,9 @@ import { dirname as dirname2, join as join4 } from "path";
|
|
|
320
380
|
function defaultStatePath() {
|
|
321
381
|
return join4(homedir3(), ".config", "squadrant", "state", "cmux-autoconfig.json");
|
|
322
382
|
}
|
|
323
|
-
function readState(
|
|
383
|
+
function readState(path29) {
|
|
324
384
|
try {
|
|
325
|
-
return JSON.parse(readFileSync4(
|
|
385
|
+
return JSON.parse(readFileSync4(path29, "utf-8"));
|
|
326
386
|
} catch {
|
|
327
387
|
return {};
|
|
328
388
|
}
|
|
@@ -543,9 +603,9 @@ var init_config_version = __esm({
|
|
|
543
603
|
|
|
544
604
|
// packages/shared/dist/lib/git-worktree.js
|
|
545
605
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
546
|
-
import
|
|
606
|
+
import path3 from "path";
|
|
547
607
|
function worktreePath(repoRoot, worktreeDir, project, name) {
|
|
548
|
-
return
|
|
608
|
+
return path3.resolve(repoRoot, worktreeDir, `${project}-${name}`);
|
|
549
609
|
}
|
|
550
610
|
function crewBranch(name) {
|
|
551
611
|
return `crew/${name}`;
|
|
@@ -579,7 +639,7 @@ var init_git_worktree = __esm({
|
|
|
579
639
|
});
|
|
580
640
|
|
|
581
641
|
// packages/shared/dist/lib/resolve-text-input.js
|
|
582
|
-
import
|
|
642
|
+
import fs3 from "fs";
|
|
583
643
|
async function readAllStdin() {
|
|
584
644
|
const chunks = [];
|
|
585
645
|
for await (const chunk of process.stdin) {
|
|
@@ -591,7 +651,7 @@ function flagName(label) {
|
|
|
591
651
|
return label === "task" ? "--task-file" : "--message-file";
|
|
592
652
|
}
|
|
593
653
|
async function resolveTextInput(opts, deps) {
|
|
594
|
-
const readFile6 = deps?.readFile ?? ((p) =>
|
|
654
|
+
const readFile6 = deps?.readFile ?? ((p) => fs3.readFileSync(p, "utf8"));
|
|
595
655
|
const readStdin3 = deps?.readStdin ?? readAllStdin;
|
|
596
656
|
if (opts.filePath) {
|
|
597
657
|
if (opts.filePath === "-") {
|
|
@@ -619,59 +679,59 @@ var init_resolve_text_input = __esm({
|
|
|
619
679
|
});
|
|
620
680
|
|
|
621
681
|
// packages/shared/dist/lib/runtime-sync.js
|
|
622
|
-
import
|
|
623
|
-
import
|
|
682
|
+
import fs4 from "fs";
|
|
683
|
+
import path4 from "path";
|
|
624
684
|
function copyIfDifferent(src, dest) {
|
|
625
|
-
if (
|
|
626
|
-
if (
|
|
685
|
+
if (fs4.existsSync(dest)) {
|
|
686
|
+
if (fs4.readFileSync(src).equals(fs4.readFileSync(dest)))
|
|
627
687
|
return false;
|
|
628
688
|
}
|
|
629
|
-
|
|
689
|
+
fs4.copyFileSync(src, dest);
|
|
630
690
|
return true;
|
|
631
691
|
}
|
|
632
692
|
function mirrorDir(src, dest) {
|
|
633
|
-
|
|
634
|
-
const srcEntries =
|
|
693
|
+
fs4.mkdirSync(dest, { recursive: true });
|
|
694
|
+
const srcEntries = fs4.readdirSync(src, { withFileTypes: true });
|
|
635
695
|
const srcNames = new Set(srcEntries.map((e) => e.name));
|
|
636
696
|
for (const entry of srcEntries) {
|
|
637
|
-
const srcPath =
|
|
638
|
-
const destPath =
|
|
697
|
+
const srcPath = path4.join(src, entry.name);
|
|
698
|
+
const destPath = path4.join(dest, entry.name);
|
|
639
699
|
if (entry.isDirectory()) {
|
|
640
700
|
mirrorDir(srcPath, destPath);
|
|
641
701
|
} else {
|
|
642
702
|
copyIfDifferent(srcPath, destPath);
|
|
643
703
|
}
|
|
644
704
|
}
|
|
645
|
-
for (const entry of
|
|
705
|
+
for (const entry of fs4.readdirSync(dest, { withFileTypes: true })) {
|
|
646
706
|
if (!srcNames.has(entry.name)) {
|
|
647
|
-
|
|
707
|
+
fs4.rmSync(path4.join(dest, entry.name), { recursive: true, force: true });
|
|
648
708
|
}
|
|
649
709
|
}
|
|
650
710
|
}
|
|
651
711
|
function mirrorFlat(src, dest, match, chmod) {
|
|
652
|
-
|
|
653
|
-
const matched =
|
|
712
|
+
fs4.mkdirSync(dest, { recursive: true });
|
|
713
|
+
const matched = fs4.readdirSync(src, { withFileTypes: true }).filter((e) => e.isFile() && match.test(e.name)).map((e) => e.name);
|
|
654
714
|
const matchedSet = new Set(matched);
|
|
655
715
|
for (const name of matched) {
|
|
656
|
-
const destPath =
|
|
657
|
-
const copied = copyIfDifferent(
|
|
716
|
+
const destPath = path4.join(dest, name);
|
|
717
|
+
const copied = copyIfDifferent(path4.join(src, name), destPath);
|
|
658
718
|
if (copied && chmod !== void 0)
|
|
659
|
-
|
|
719
|
+
fs4.chmodSync(destPath, chmod);
|
|
660
720
|
}
|
|
661
|
-
for (const entry of
|
|
721
|
+
for (const entry of fs4.readdirSync(dest, { withFileTypes: true })) {
|
|
662
722
|
if (!matchedSet.has(entry.name)) {
|
|
663
|
-
|
|
723
|
+
fs4.rmSync(path4.join(dest, entry.name), { recursive: true, force: true });
|
|
664
724
|
}
|
|
665
725
|
}
|
|
666
726
|
}
|
|
667
727
|
function ensureRuntimeSynced(opts) {
|
|
668
728
|
const targets = opts.targets ?? MANAGED_TARGETS;
|
|
669
729
|
for (const t of targets) {
|
|
670
|
-
const srcDir =
|
|
730
|
+
const srcDir = path4.join(opts.sourceRoot, t.srcRel);
|
|
671
731
|
try {
|
|
672
|
-
if (!
|
|
732
|
+
if (!fs4.existsSync(srcDir))
|
|
673
733
|
continue;
|
|
674
|
-
const destDir =
|
|
734
|
+
const destDir = path4.join(opts.runtimeRoot, t.name);
|
|
675
735
|
if (t.mode === "tree") {
|
|
676
736
|
mirrorDir(srcDir, destDir);
|
|
677
737
|
} else {
|
|
@@ -735,8 +795,8 @@ var init_tool_compat = __esm({
|
|
|
735
795
|
});
|
|
736
796
|
|
|
737
797
|
// packages/shared/dist/lib/canonical-source.js
|
|
738
|
-
import
|
|
739
|
-
import
|
|
798
|
+
import fs5 from "fs";
|
|
799
|
+
import path5 from "path";
|
|
740
800
|
function parseSkill(raw) {
|
|
741
801
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
742
802
|
if (!match)
|
|
@@ -777,10 +837,10 @@ async function readSkills(driver, skillsDir) {
|
|
|
777
837
|
function readRoleTemplates(opts) {
|
|
778
838
|
if (!opts.pkgRoot)
|
|
779
839
|
return "";
|
|
780
|
-
const reader = opts.readFile ?? ((p) =>
|
|
840
|
+
const reader = opts.readFile ?? ((p) => fs5.readFileSync(p, "utf-8"));
|
|
781
841
|
const sections = [];
|
|
782
842
|
for (const { file, heading } of ROLE_TEMPLATES) {
|
|
783
|
-
const full =
|
|
843
|
+
const full = path5.join(opts.pkgRoot, "templates", file);
|
|
784
844
|
let body = "";
|
|
785
845
|
try {
|
|
786
846
|
body = reader(full);
|
|
@@ -817,8 +877,8 @@ var init_canonical_source = __esm({
|
|
|
817
877
|
|
|
818
878
|
// packages/shared/dist/lib/daily-logs.js
|
|
819
879
|
import { execSync } from "child_process";
|
|
820
|
-
import
|
|
821
|
-
import
|
|
880
|
+
import fs6 from "fs";
|
|
881
|
+
import path6 from "path";
|
|
822
882
|
import matter from "gray-matter";
|
|
823
883
|
function iso(d) {
|
|
824
884
|
return d.toISOString().slice(0, 10);
|
|
@@ -870,7 +930,7 @@ function getGitCommits(projectPath, dateStr) {
|
|
|
870
930
|
}
|
|
871
931
|
function getGitCommitsInRange(projectPath, since, until) {
|
|
872
932
|
const resolved = resolveHome(projectPath);
|
|
873
|
-
if (!
|
|
933
|
+
if (!fs6.existsSync(path6.join(resolved, ".git")))
|
|
874
934
|
return [];
|
|
875
935
|
const untilArg = until ? ` --until="${until}"` : "";
|
|
876
936
|
try {
|
|
@@ -884,7 +944,7 @@ function getGitCommitsInRange(projectPath, since, until) {
|
|
|
884
944
|
}
|
|
885
945
|
function getMergedPRsInRange(projectPath, since, until) {
|
|
886
946
|
const resolved = resolveHome(projectPath);
|
|
887
|
-
if (!
|
|
947
|
+
if (!fs6.existsSync(path6.join(resolved, ".git")))
|
|
888
948
|
return [];
|
|
889
949
|
const untilArg = until ? ` --until="${until}"` : "";
|
|
890
950
|
try {
|
|
@@ -924,10 +984,27 @@ var init_vault_layout = __esm({
|
|
|
924
984
|
}
|
|
925
985
|
});
|
|
926
986
|
|
|
987
|
+
// packages/shared/dist/daemon-keys.js
|
|
988
|
+
function isDaemonCachedKey(dottedKey) {
|
|
989
|
+
return DAEMON_CACHED_PREFIXES.some((p) => dottedKey === p || dottedKey.startsWith(p));
|
|
990
|
+
}
|
|
991
|
+
var DAEMON_CACHED_PREFIXES;
|
|
992
|
+
var init_daemon_keys = __esm({
|
|
993
|
+
"packages/shared/dist/daemon-keys.js"() {
|
|
994
|
+
DAEMON_CACHED_PREFIXES = [
|
|
995
|
+
"telegram.",
|
|
996
|
+
"defaults.taskTimeoutMs",
|
|
997
|
+
"defaults.cmuxEventsBridge",
|
|
998
|
+
"projects."
|
|
999
|
+
];
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
|
|
927
1003
|
// packages/shared/dist/index.js
|
|
928
1004
|
var init_dist = __esm({
|
|
929
1005
|
"packages/shared/dist/index.js"() {
|
|
930
1006
|
init_config();
|
|
1007
|
+
init_project_config();
|
|
931
1008
|
init_effort();
|
|
932
1009
|
init_runtime();
|
|
933
1010
|
init_control();
|
|
@@ -947,6 +1024,7 @@ var init_dist = __esm({
|
|
|
947
1024
|
init_canonical_source();
|
|
948
1025
|
init_daily_logs();
|
|
949
1026
|
init_vault_layout();
|
|
1027
|
+
init_daemon_keys();
|
|
950
1028
|
}
|
|
951
1029
|
});
|
|
952
1030
|
|
|
@@ -978,7 +1056,7 @@ var init_daemon = __esm({
|
|
|
978
1056
|
});
|
|
979
1057
|
|
|
980
1058
|
// packages/core/dist/mailbox.js
|
|
981
|
-
import { promises as
|
|
1059
|
+
import { promises as fs7 } from "fs";
|
|
982
1060
|
import { join as join5 } from "path";
|
|
983
1061
|
import { randomUUID } from "crypto";
|
|
984
1062
|
var init_mailbox = __esm({
|
|
@@ -1131,10 +1209,10 @@ function daemonEntryPath() {
|
|
|
1131
1209
|
function xmlEscape(s) {
|
|
1132
1210
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1133
1211
|
}
|
|
1134
|
-
function sanitizePathForPlist(
|
|
1212
|
+
function sanitizePathForPlist(path29) {
|
|
1135
1213
|
const seen = /* @__PURE__ */ new Set();
|
|
1136
1214
|
const stable = [];
|
|
1137
|
-
for (const p of
|
|
1215
|
+
for (const p of path29.split(":")) {
|
|
1138
1216
|
if (!p)
|
|
1139
1217
|
continue;
|
|
1140
1218
|
if (p.includes("/.claude/plugins/"))
|
|
@@ -1202,6 +1280,9 @@ function renderPlist(nodeBin, daemonEntry, pathEnv = "") {
|
|
|
1202
1280
|
function programArgsBlock(nodeBin, daemonEntry) {
|
|
1203
1281
|
return `<array><string>${xmlEscape(nodeBin)}</string><string>${xmlEscape(daemonEntry)}</string></array>`;
|
|
1204
1282
|
}
|
|
1283
|
+
function kickstartArgv(target, plistChanged) {
|
|
1284
|
+
return plistChanged ? ["kickstart", "-k", target] : ["kickstart", target];
|
|
1285
|
+
}
|
|
1205
1286
|
function daemonLockPath() {
|
|
1206
1287
|
return join7(homedir4(), ".config", "squadrant", "daemon.lock");
|
|
1207
1288
|
}
|
|
@@ -1423,35 +1504,35 @@ var init_start = __esm({
|
|
|
1423
1504
|
|
|
1424
1505
|
// packages/core/dist/session-freshness.js
|
|
1425
1506
|
import crypto from "crypto";
|
|
1426
|
-
import
|
|
1427
|
-
import
|
|
1507
|
+
import fs8 from "fs";
|
|
1508
|
+
import path7 from "path";
|
|
1428
1509
|
function loadSessions(sessionsPath) {
|
|
1429
1510
|
try {
|
|
1430
|
-
return JSON.parse(
|
|
1511
|
+
return JSON.parse(fs8.readFileSync(sessionsPath, "utf-8"));
|
|
1431
1512
|
} catch {
|
|
1432
1513
|
return { workspaces: {} };
|
|
1433
1514
|
}
|
|
1434
1515
|
}
|
|
1435
1516
|
function saveSessions(sessionsPath, sessions) {
|
|
1436
|
-
const dir =
|
|
1437
|
-
|
|
1438
|
-
|
|
1517
|
+
const dir = path7.dirname(sessionsPath);
|
|
1518
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
1519
|
+
fs8.writeFileSync(sessionsPath, JSON.stringify(sessions, null, 2) + "\n");
|
|
1439
1520
|
}
|
|
1440
1521
|
function computeTemplateHash(role, templatesDir) {
|
|
1441
1522
|
const hash = crypto.createHash("sha256");
|
|
1442
|
-
const roleFile =
|
|
1443
|
-
const legacyRoleFile =
|
|
1444
|
-
if (
|
|
1445
|
-
hash.update(
|
|
1446
|
-
} else if (
|
|
1447
|
-
hash.update(
|
|
1523
|
+
const roleFile = path7.join(templatesDir, `${role}.claude.md`);
|
|
1524
|
+
const legacyRoleFile = path7.join(templatesDir, `${role}.CLAUDE.md`);
|
|
1525
|
+
if (fs8.existsSync(roleFile)) {
|
|
1526
|
+
hash.update(fs8.readFileSync(roleFile, "utf-8"));
|
|
1527
|
+
} else if (fs8.existsSync(legacyRoleFile)) {
|
|
1528
|
+
hash.update(fs8.readFileSync(legacyRoleFile, "utf-8"));
|
|
1448
1529
|
}
|
|
1449
|
-
const pluginSkillsDir =
|
|
1450
|
-
if (
|
|
1451
|
-
for (const skill of
|
|
1452
|
-
const skillFile =
|
|
1453
|
-
if (
|
|
1454
|
-
hash.update(
|
|
1530
|
+
const pluginSkillsDir = path7.join(templatesDir, "..", "plugin", "skills");
|
|
1531
|
+
if (fs8.existsSync(pluginSkillsDir)) {
|
|
1532
|
+
for (const skill of fs8.readdirSync(pluginSkillsDir).sort()) {
|
|
1533
|
+
const skillFile = path7.join(pluginSkillsDir, skill, "SKILL.md");
|
|
1534
|
+
if (fs8.existsSync(skillFile)) {
|
|
1535
|
+
hash.update(fs8.readFileSync(skillFile, "utf-8"));
|
|
1455
1536
|
}
|
|
1456
1537
|
}
|
|
1457
1538
|
}
|
|
@@ -1571,6 +1652,25 @@ var init_crew_lifecycle = __esm({
|
|
|
1571
1652
|
}
|
|
1572
1653
|
});
|
|
1573
1654
|
|
|
1655
|
+
// packages/core/dist/telegram/bot-commands.js
|
|
1656
|
+
var BOT_COMMANDS;
|
|
1657
|
+
var init_bot_commands = __esm({
|
|
1658
|
+
"packages/core/dist/telegram/bot-commands.js"() {
|
|
1659
|
+
BOT_COMMANDS = [
|
|
1660
|
+
{ command: "status", description: "squadrant status" },
|
|
1661
|
+
{ command: "projects", description: "list registered projects" },
|
|
1662
|
+
{ command: "crews", description: "list crews for a project" },
|
|
1663
|
+
{ command: "launch", description: "launch a project's captain" },
|
|
1664
|
+
{ command: "effort", description: "set effort: max | balance | low" },
|
|
1665
|
+
{ command: "spawn", description: "spawn a crew (guided)" },
|
|
1666
|
+
{ command: "notify", description: "notification panel for a project topic" },
|
|
1667
|
+
{ command: "mute", description: "mute a project's topic" },
|
|
1668
|
+
{ command: "unmute", description: "unmute a project's topic" },
|
|
1669
|
+
{ command: "help", description: "list commands" }
|
|
1670
|
+
];
|
|
1671
|
+
}
|
|
1672
|
+
});
|
|
1673
|
+
|
|
1574
1674
|
// packages/core/dist/telegram/auth.js
|
|
1575
1675
|
var init_auth = __esm({
|
|
1576
1676
|
"packages/core/dist/telegram/auth.js"() {
|
|
@@ -1604,35 +1704,47 @@ var init_format = __esm({
|
|
|
1604
1704
|
});
|
|
1605
1705
|
|
|
1606
1706
|
// packages/core/dist/telegram/state.js
|
|
1607
|
-
import
|
|
1608
|
-
import
|
|
1707
|
+
import fs9 from "fs";
|
|
1708
|
+
import path8 from "path";
|
|
1609
1709
|
function statePath(stateRoot) {
|
|
1610
|
-
return
|
|
1710
|
+
return path8.join(stateRoot, "telegram-state.json");
|
|
1611
1711
|
}
|
|
1612
1712
|
function topicKey(project, scope = "project") {
|
|
1613
1713
|
return `${project}::${scope}`;
|
|
1614
1714
|
}
|
|
1615
1715
|
function loadState(stateRoot) {
|
|
1616
1716
|
try {
|
|
1617
|
-
const raw =
|
|
1717
|
+
const raw = fs9.readFileSync(statePath(stateRoot), "utf-8");
|
|
1618
1718
|
const data = JSON.parse(raw);
|
|
1619
|
-
|
|
1719
|
+
const result = {
|
|
1620
1720
|
offset: typeof data.offset === "number" ? data.offset : 0,
|
|
1621
|
-
topics: data.topics ?? {}
|
|
1721
|
+
topics: data.topics ?? {},
|
|
1722
|
+
notify: data.notify ?? {}
|
|
1622
1723
|
};
|
|
1724
|
+
if (typeof data.lastUserId === "number")
|
|
1725
|
+
result.lastUserId = data.lastUserId;
|
|
1726
|
+
return result;
|
|
1623
1727
|
} catch {
|
|
1624
|
-
return { offset: 0, topics: {} };
|
|
1728
|
+
return { offset: 0, topics: {}, notify: {} };
|
|
1625
1729
|
}
|
|
1626
1730
|
}
|
|
1627
1731
|
function saveState(stateRoot, s) {
|
|
1628
|
-
|
|
1629
|
-
|
|
1732
|
+
fs9.mkdirSync(stateRoot, { recursive: true });
|
|
1733
|
+
fs9.writeFileSync(statePath(stateRoot), JSON.stringify(s, null, 2) + "\n");
|
|
1630
1734
|
}
|
|
1631
1735
|
function setTopic(stateRoot, project, topicId, scope = "project") {
|
|
1632
1736
|
const s = loadState(stateRoot);
|
|
1633
1737
|
s.topics[topicKey(project, scope)] = topicId;
|
|
1634
1738
|
saveState(stateRoot, s);
|
|
1635
1739
|
}
|
|
1740
|
+
function isNotifyActive(stateRoot, project) {
|
|
1741
|
+
return loadState(stateRoot).notify[project] === true;
|
|
1742
|
+
}
|
|
1743
|
+
function setNotify(stateRoot, project, active) {
|
|
1744
|
+
const s = loadState(stateRoot);
|
|
1745
|
+
s.notify[project] = active;
|
|
1746
|
+
saveState(stateRoot, s);
|
|
1747
|
+
}
|
|
1636
1748
|
var init_state = __esm({
|
|
1637
1749
|
"packages/core/dist/telegram/state.js"() {
|
|
1638
1750
|
}
|
|
@@ -1664,15 +1776,29 @@ function createTelegramClient(opts) {
|
|
|
1664
1776
|
getUpdates(offset, timeoutSec = 50) {
|
|
1665
1777
|
return call("getUpdates", { offset, timeout: timeoutSec });
|
|
1666
1778
|
},
|
|
1667
|
-
async sendMessage(chatId, threadId, text) {
|
|
1779
|
+
async sendMessage(chatId, threadId, text, replyMarkup) {
|
|
1668
1780
|
const body = { chat_id: chatId, text };
|
|
1669
1781
|
if (threadId !== void 0)
|
|
1670
1782
|
body.message_thread_id = threadId;
|
|
1783
|
+
if (replyMarkup !== void 0)
|
|
1784
|
+
body.reply_markup = replyMarkup;
|
|
1671
1785
|
await call("sendMessage", body);
|
|
1672
1786
|
},
|
|
1787
|
+
async answerCallbackQuery(callbackQueryId, text) {
|
|
1788
|
+
const body = { callback_query_id: callbackQueryId };
|
|
1789
|
+
if (text !== void 0)
|
|
1790
|
+
body.text = text;
|
|
1791
|
+
await call("answerCallbackQuery", body);
|
|
1792
|
+
},
|
|
1793
|
+
async editMessageReplyMarkup(chatId, messageId, replyMarkup) {
|
|
1794
|
+
await call("editMessageReplyMarkup", { chat_id: chatId, message_id: messageId, reply_markup: replyMarkup });
|
|
1795
|
+
},
|
|
1673
1796
|
async createForumTopic(chatId, name) {
|
|
1674
1797
|
const r = await call("createForumTopic", { chat_id: chatId, name });
|
|
1675
1798
|
return r.message_thread_id;
|
|
1799
|
+
},
|
|
1800
|
+
async setMyCommands(commands) {
|
|
1801
|
+
await call("setMyCommands", { commands });
|
|
1676
1802
|
}
|
|
1677
1803
|
};
|
|
1678
1804
|
}
|
|
@@ -1681,18 +1807,49 @@ var init_client = __esm({
|
|
|
1681
1807
|
}
|
|
1682
1808
|
});
|
|
1683
1809
|
|
|
1810
|
+
// packages/core/dist/telegram/panels.js
|
|
1811
|
+
var init_panels = __esm({
|
|
1812
|
+
"packages/core/dist/telegram/panels.js"() {
|
|
1813
|
+
}
|
|
1814
|
+
});
|
|
1815
|
+
|
|
1816
|
+
// packages/core/dist/telegram/tiers.js
|
|
1817
|
+
var DONE_ONLY, ALERTS;
|
|
1818
|
+
var init_tiers = __esm({
|
|
1819
|
+
"packages/core/dist/telegram/tiers.js"() {
|
|
1820
|
+
DONE_ONLY = /* @__PURE__ */ new Set(["task.done", "task.failed"]);
|
|
1821
|
+
ALERTS = /* @__PURE__ */ new Set([
|
|
1822
|
+
...DONE_ONLY,
|
|
1823
|
+
"task.blocked",
|
|
1824
|
+
"task.approval.requested",
|
|
1825
|
+
"task.input.requested",
|
|
1826
|
+
"task.timeout"
|
|
1827
|
+
]);
|
|
1828
|
+
}
|
|
1829
|
+
});
|
|
1830
|
+
|
|
1684
1831
|
// packages/core/dist/telegram/bridge.js
|
|
1832
|
+
import os3 from "os";
|
|
1833
|
+
import path9 from "path";
|
|
1685
1834
|
var init_bridge = __esm({
|
|
1686
1835
|
"packages/core/dist/telegram/bridge.js"() {
|
|
1836
|
+
init_dist();
|
|
1687
1837
|
init_auth();
|
|
1688
1838
|
init_commands();
|
|
1689
1839
|
init_format();
|
|
1840
|
+
init_panels();
|
|
1690
1841
|
init_state();
|
|
1842
|
+
init_tiers();
|
|
1691
1843
|
}
|
|
1692
1844
|
});
|
|
1693
1845
|
|
|
1694
1846
|
// packages/core/dist/telegram/setup.js
|
|
1695
|
-
import
|
|
1847
|
+
import fs10 from "fs";
|
|
1848
|
+
function resolveSetupGroup(existingSupergroupId, opts) {
|
|
1849
|
+
if (existingSupergroupId !== void 0 && !opts.redetect)
|
|
1850
|
+
return "reuse";
|
|
1851
|
+
return "detect";
|
|
1852
|
+
}
|
|
1696
1853
|
async function detectGroupAndUser(client, opts = {}) {
|
|
1697
1854
|
const timeoutMs = opts.timeoutMs ?? 6e4;
|
|
1698
1855
|
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
@@ -1715,7 +1872,7 @@ function writeTelegramConfig(configPath, opts) {
|
|
|
1715
1872
|
let config;
|
|
1716
1873
|
let raw = null;
|
|
1717
1874
|
try {
|
|
1718
|
-
raw =
|
|
1875
|
+
raw = fs10.readFileSync(configPath, "utf-8");
|
|
1719
1876
|
} catch (err) {
|
|
1720
1877
|
if (err.code !== "ENOENT") {
|
|
1721
1878
|
throw new Error(`refusing to overwrite unreadable config at ${configPath}: ${String(err)}`);
|
|
@@ -1743,7 +1900,7 @@ function writeTelegramConfig(configPath, opts) {
|
|
|
1743
1900
|
if (remoteControl !== void 0)
|
|
1744
1901
|
next.remoteControl = remoteControl;
|
|
1745
1902
|
config.telegram = next;
|
|
1746
|
-
|
|
1903
|
+
fs10.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
1747
1904
|
}
|
|
1748
1905
|
var init_setup = __esm({
|
|
1749
1906
|
"packages/core/dist/telegram/setup.js"() {
|
|
@@ -1753,6 +1910,7 @@ var init_setup = __esm({
|
|
|
1753
1910
|
// packages/core/dist/telegram/index.js
|
|
1754
1911
|
var init_telegram = __esm({
|
|
1755
1912
|
"packages/core/dist/telegram/index.js"() {
|
|
1913
|
+
init_bot_commands();
|
|
1756
1914
|
init_auth();
|
|
1757
1915
|
init_commands();
|
|
1758
1916
|
init_ensure_captain();
|
|
@@ -2308,13 +2466,13 @@ var init_notifiers = __esm({
|
|
|
2308
2466
|
});
|
|
2309
2467
|
|
|
2310
2468
|
// packages/workspaces/dist/workspaces/obsidian.js
|
|
2311
|
-
import
|
|
2469
|
+
import fs11 from "fs/promises";
|
|
2312
2470
|
import { existsSync as existsSync8 } from "fs";
|
|
2313
|
-
import
|
|
2471
|
+
import path10 from "path";
|
|
2314
2472
|
function resolveInRoot(root, relative) {
|
|
2315
|
-
const joined =
|
|
2316
|
-
const normalized =
|
|
2317
|
-
if (joined !==
|
|
2473
|
+
const joined = path10.resolve(root, relative);
|
|
2474
|
+
const normalized = path10.resolve(root) + path10.sep;
|
|
2475
|
+
if (joined !== path10.resolve(root) && !joined.startsWith(normalized)) {
|
|
2318
2476
|
throw new Error(`Path '${relative}' escapes workspace root`);
|
|
2319
2477
|
}
|
|
2320
2478
|
return joined;
|
|
@@ -2333,16 +2491,16 @@ function createObsidianDriver(scope) {
|
|
|
2333
2491
|
};
|
|
2334
2492
|
},
|
|
2335
2493
|
async read(rel) {
|
|
2336
|
-
return
|
|
2494
|
+
return fs11.readFile(resolveInRoot(root, rel), "utf-8");
|
|
2337
2495
|
},
|
|
2338
2496
|
async write(rel, content) {
|
|
2339
2497
|
const abs = resolveInRoot(root, rel);
|
|
2340
|
-
await
|
|
2341
|
-
await
|
|
2498
|
+
await fs11.mkdir(path10.dirname(abs), { recursive: true });
|
|
2499
|
+
await fs11.writeFile(abs, content);
|
|
2342
2500
|
},
|
|
2343
2501
|
async exists(rel) {
|
|
2344
2502
|
try {
|
|
2345
|
-
await
|
|
2503
|
+
await fs11.access(resolveInRoot(root, rel));
|
|
2346
2504
|
return true;
|
|
2347
2505
|
} catch {
|
|
2348
2506
|
return false;
|
|
@@ -2350,13 +2508,13 @@ function createObsidianDriver(scope) {
|
|
|
2350
2508
|
},
|
|
2351
2509
|
async list(rel) {
|
|
2352
2510
|
try {
|
|
2353
|
-
return await
|
|
2511
|
+
return await fs11.readdir(resolveInRoot(root, rel));
|
|
2354
2512
|
} catch {
|
|
2355
2513
|
return [];
|
|
2356
2514
|
}
|
|
2357
2515
|
},
|
|
2358
2516
|
async mkdir(rel) {
|
|
2359
|
-
await
|
|
2517
|
+
await fs11.mkdir(resolveInRoot(root, rel), { recursive: true });
|
|
2360
2518
|
}
|
|
2361
2519
|
};
|
|
2362
2520
|
}
|
|
@@ -3025,8 +3183,8 @@ var init_registry4 = __esm({
|
|
|
3025
3183
|
});
|
|
3026
3184
|
|
|
3027
3185
|
// packages/agents/dist/drivers/launch-cmd.js
|
|
3028
|
-
import
|
|
3029
|
-
import
|
|
3186
|
+
import fs12 from "fs";
|
|
3187
|
+
import path11 from "path";
|
|
3030
3188
|
function buildAgentCmd(agentName, registry, role, fresh, permissionMode, model, templatesDir) {
|
|
3031
3189
|
const driver = registry.getDriver(agentName);
|
|
3032
3190
|
if (driver.name === "claude") {
|
|
@@ -3042,27 +3200,27 @@ function buildAgentCmd(agentName, registry, role, fresh, permissionMode, model,
|
|
|
3042
3200
|
cmd += ` --model ${model}`;
|
|
3043
3201
|
}
|
|
3044
3202
|
if (templatesDir) {
|
|
3045
|
-
const roleFile2 =
|
|
3046
|
-
const legacyRoleFile =
|
|
3047
|
-
const actualRoleFile =
|
|
3203
|
+
const roleFile2 = path11.join(templatesDir, `${role}.claude.md`);
|
|
3204
|
+
const legacyRoleFile = path11.join(templatesDir, `${role}.CLAUDE.md`);
|
|
3205
|
+
const actualRoleFile = fs12.existsSync(roleFile2) ? roleFile2 : fs12.existsSync(legacyRoleFile) ? legacyRoleFile : null;
|
|
3048
3206
|
if (actualRoleFile) {
|
|
3049
3207
|
cmd += ` --append-system-prompt-file ${actualRoleFile}`;
|
|
3050
3208
|
}
|
|
3051
|
-
const pluginDir =
|
|
3052
|
-
if (
|
|
3209
|
+
const pluginDir = path11.join(templatesDir, "..", "plugin");
|
|
3210
|
+
if (fs12.existsSync(pluginDir)) {
|
|
3053
3211
|
cmd += ` --plugin-dir ${pluginDir}`;
|
|
3054
3212
|
}
|
|
3055
3213
|
}
|
|
3056
3214
|
return cmd;
|
|
3057
3215
|
}
|
|
3058
|
-
const roleFile = templatesDir ?
|
|
3216
|
+
const roleFile = templatesDir ? path11.join(templatesDir, `${role}.${driver.templateSuffix}.md`) : void 0;
|
|
3059
3217
|
return driver.buildCommand({
|
|
3060
3218
|
prompt: `You are a squadrant ${role}. Read your instructions from ${roleFile ?? role} and begin.`,
|
|
3061
3219
|
workdir: process.cwd(),
|
|
3062
3220
|
role,
|
|
3063
3221
|
model,
|
|
3064
3222
|
autoApprove: true,
|
|
3065
|
-
promptFile: roleFile &&
|
|
3223
|
+
promptFile: roleFile && fs12.existsSync(roleFile) ? roleFile : void 0
|
|
3066
3224
|
});
|
|
3067
3225
|
}
|
|
3068
3226
|
var init_launch_cmd = __esm({
|
|
@@ -3085,8 +3243,8 @@ var init_drivers = __esm({
|
|
|
3085
3243
|
|
|
3086
3244
|
// packages/agents/dist/projection/cursor.js
|
|
3087
3245
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
3088
|
-
import
|
|
3089
|
-
import
|
|
3246
|
+
import path12 from "path";
|
|
3247
|
+
import os4 from "os";
|
|
3090
3248
|
function renderMdc(source) {
|
|
3091
3249
|
const skillSections = source.skills.map((s) => `## Skill: ${s.name}
|
|
3092
3250
|
|
|
@@ -3134,7 +3292,7 @@ function createCursorEmitter() {
|
|
|
3134
3292
|
if (scope === "user") {
|
|
3135
3293
|
return [
|
|
3136
3294
|
{
|
|
3137
|
-
path:
|
|
3295
|
+
path: path12.join(os4.homedir(), ".cursor/rules/squadrant-global.mdc"),
|
|
3138
3296
|
shared: false,
|
|
3139
3297
|
format: "mdc"
|
|
3140
3298
|
}
|
|
@@ -3144,7 +3302,7 @@ function createCursorEmitter() {
|
|
|
3144
3302
|
return [];
|
|
3145
3303
|
return [
|
|
3146
3304
|
{
|
|
3147
|
-
path:
|
|
3305
|
+
path: path12.join(projectRoot, ".cursor/rules/squadrant.mdc"),
|
|
3148
3306
|
shared: false,
|
|
3149
3307
|
format: "mdc"
|
|
3150
3308
|
}
|
|
@@ -3161,7 +3319,7 @@ function createCursorEmitter() {
|
|
|
3161
3319
|
diff: buildDiff(existing, generated)
|
|
3162
3320
|
};
|
|
3163
3321
|
}
|
|
3164
|
-
await mkdir(
|
|
3322
|
+
await mkdir(path12.dirname(dest.path), { recursive: true });
|
|
3165
3323
|
await writeFile(dest.path, generated, "utf-8");
|
|
3166
3324
|
return {
|
|
3167
3325
|
written: true,
|
|
@@ -3212,8 +3370,8 @@ var init_marker = __esm({
|
|
|
3212
3370
|
|
|
3213
3371
|
// packages/agents/dist/projection/codex.js
|
|
3214
3372
|
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
3215
|
-
import
|
|
3216
|
-
import
|
|
3373
|
+
import path13 from "path";
|
|
3374
|
+
import os5 from "os";
|
|
3217
3375
|
function renderMarkdown(source) {
|
|
3218
3376
|
const skillSections = source.skills.map((s) => `## Skill: ${s.name}
|
|
3219
3377
|
|
|
@@ -3237,7 +3395,7 @@ function createCodexEmitter() {
|
|
|
3237
3395
|
destinations(scope, projectRoot) {
|
|
3238
3396
|
if (scope === "user") {
|
|
3239
3397
|
return [{
|
|
3240
|
-
path:
|
|
3398
|
+
path: path13.join(os5.homedir(), ".codex/AGENTS.md"),
|
|
3241
3399
|
shared: true,
|
|
3242
3400
|
format: "markdown"
|
|
3243
3401
|
}];
|
|
@@ -3245,7 +3403,7 @@ function createCodexEmitter() {
|
|
|
3245
3403
|
if (!projectRoot)
|
|
3246
3404
|
return [];
|
|
3247
3405
|
return [{
|
|
3248
|
-
path:
|
|
3406
|
+
path: path13.join(projectRoot, "AGENTS.md"),
|
|
3249
3407
|
shared: true,
|
|
3250
3408
|
format: "markdown"
|
|
3251
3409
|
}];
|
|
@@ -3266,7 +3424,7 @@ ${existing ?? ""}
|
|
|
3266
3424
|
${generated}`
|
|
3267
3425
|
};
|
|
3268
3426
|
}
|
|
3269
|
-
await mkdir2(
|
|
3427
|
+
await mkdir2(path13.dirname(dest.path), { recursive: true });
|
|
3270
3428
|
await writeFile2(dest.path, generated, "utf-8");
|
|
3271
3429
|
return {
|
|
3272
3430
|
written: true,
|
|
@@ -3284,8 +3442,8 @@ var init_codex2 = __esm({
|
|
|
3284
3442
|
|
|
3285
3443
|
// packages/agents/dist/projection/gemini.js
|
|
3286
3444
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
3287
|
-
import
|
|
3288
|
-
import
|
|
3445
|
+
import path14 from "path";
|
|
3446
|
+
import os6 from "os";
|
|
3289
3447
|
function renderMarkdown2(source) {
|
|
3290
3448
|
const skillSections = source.skills.map((s) => `## Skill: ${s.name}
|
|
3291
3449
|
|
|
@@ -3309,7 +3467,7 @@ function createGeminiEmitter() {
|
|
|
3309
3467
|
destinations(scope, projectRoot) {
|
|
3310
3468
|
if (scope === "user") {
|
|
3311
3469
|
return [{
|
|
3312
|
-
path:
|
|
3470
|
+
path: path14.join(os6.homedir(), ".gemini/GEMINI.md"),
|
|
3313
3471
|
shared: true,
|
|
3314
3472
|
format: "markdown"
|
|
3315
3473
|
}];
|
|
@@ -3317,7 +3475,7 @@ function createGeminiEmitter() {
|
|
|
3317
3475
|
if (!projectRoot)
|
|
3318
3476
|
return [];
|
|
3319
3477
|
return [{
|
|
3320
|
-
path:
|
|
3478
|
+
path: path14.join(projectRoot, "GEMINI.md"),
|
|
3321
3479
|
shared: true,
|
|
3322
3480
|
format: "markdown"
|
|
3323
3481
|
}];
|
|
@@ -3338,7 +3496,7 @@ ${existing ?? ""}
|
|
|
3338
3496
|
${generated}`
|
|
3339
3497
|
};
|
|
3340
3498
|
}
|
|
3341
|
-
await mkdir3(
|
|
3499
|
+
await mkdir3(path14.dirname(dest.path), { recursive: true });
|
|
3342
3500
|
await writeFile3(dest.path, generated, "utf-8");
|
|
3343
3501
|
return {
|
|
3344
3502
|
written: true,
|
|
@@ -3356,8 +3514,8 @@ var init_gemini2 = __esm({
|
|
|
3356
3514
|
|
|
3357
3515
|
// packages/agents/dist/projection/opencode.js
|
|
3358
3516
|
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
3359
|
-
import
|
|
3360
|
-
import
|
|
3517
|
+
import path15 from "path";
|
|
3518
|
+
import os7 from "os";
|
|
3361
3519
|
function renderMarkdown3(source) {
|
|
3362
3520
|
const skillSections = source.skills.map((s) => `## Skill: ${s.name}
|
|
3363
3521
|
|
|
@@ -3381,7 +3539,7 @@ function createOpencodeEmitter() {
|
|
|
3381
3539
|
destinations(scope, projectRoot) {
|
|
3382
3540
|
if (scope === "user") {
|
|
3383
3541
|
return [{
|
|
3384
|
-
path:
|
|
3542
|
+
path: path15.join(os7.homedir(), ".config", "opencode", "AGENTS.md"),
|
|
3385
3543
|
shared: true,
|
|
3386
3544
|
format: "markdown"
|
|
3387
3545
|
}];
|
|
@@ -3389,7 +3547,7 @@ function createOpencodeEmitter() {
|
|
|
3389
3547
|
if (!projectRoot)
|
|
3390
3548
|
return [];
|
|
3391
3549
|
return [{
|
|
3392
|
-
path:
|
|
3550
|
+
path: path15.join(projectRoot, "AGENTS.md"),
|
|
3393
3551
|
shared: true,
|
|
3394
3552
|
format: "markdown"
|
|
3395
3553
|
}];
|
|
@@ -3410,7 +3568,7 @@ ${existing ?? ""}
|
|
|
3410
3568
|
${generated}`
|
|
3411
3569
|
};
|
|
3412
3570
|
}
|
|
3413
|
-
await mkdir4(
|
|
3571
|
+
await mkdir4(path15.dirname(dest.path), { recursive: true });
|
|
3414
3572
|
await writeFile4(dest.path, generated, "utf-8");
|
|
3415
3573
|
return {
|
|
3416
3574
|
written: true,
|
|
@@ -4275,8 +4433,8 @@ function resolveLastAssistantText(payload) {
|
|
|
4275
4433
|
const derived = deriveTranscriptPath(p?.session_id, cwd);
|
|
4276
4434
|
if (derived)
|
|
4277
4435
|
candidates.push(derived);
|
|
4278
|
-
for (const
|
|
4279
|
-
const text = readLastAssistantText(
|
|
4436
|
+
for (const path29 of candidates) {
|
|
4437
|
+
const text = readLastAssistantText(path29);
|
|
4280
4438
|
if (text != null)
|
|
4281
4439
|
return text;
|
|
4282
4440
|
}
|
|
@@ -4676,10 +4834,10 @@ var init_dist4 = __esm({
|
|
|
4676
4834
|
init_dist();
|
|
4677
4835
|
init_dist2();
|
|
4678
4836
|
import { Command as Command28 } from "commander";
|
|
4679
|
-
import { readFileSync as readFileSync11, existsSync as
|
|
4837
|
+
import { readFileSync as readFileSync11, existsSync as existsSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
4680
4838
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
4681
|
-
import { dirname as dirname7, join as
|
|
4682
|
-
import { homedir as
|
|
4839
|
+
import { dirname as dirname7, join as join23 } from "path";
|
|
4840
|
+
import { homedir as homedir15 } from "os";
|
|
4683
4841
|
|
|
4684
4842
|
// packages/cli/src/commands/doctor.ts
|
|
4685
4843
|
init_dist();
|
|
@@ -4688,9 +4846,9 @@ init_dist();
|
|
|
4688
4846
|
init_dist3();
|
|
4689
4847
|
import { Command } from "commander";
|
|
4690
4848
|
import { execSync as execSync8 } from "child_process";
|
|
4691
|
-
import
|
|
4849
|
+
import fs13 from "fs";
|
|
4692
4850
|
import { stat } from "fs/promises";
|
|
4693
|
-
import
|
|
4851
|
+
import path16 from "path";
|
|
4694
4852
|
import chalk3 from "chalk";
|
|
4695
4853
|
|
|
4696
4854
|
// packages/cli/src/commands/health-view.ts
|
|
@@ -4792,7 +4950,7 @@ function settingsHaveAgentTeams() {
|
|
|
4792
4950
|
try {
|
|
4793
4951
|
const home = process.env.HOME || "";
|
|
4794
4952
|
const settings = JSON.parse(
|
|
4795
|
-
|
|
4953
|
+
fs13.readFileSync(`${home}/.claude/settings.json`, "utf-8")
|
|
4796
4954
|
);
|
|
4797
4955
|
return settings?.env?.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS === "1";
|
|
4798
4956
|
} catch {
|
|
@@ -4803,7 +4961,7 @@ function pluginInstalled(pluginKey) {
|
|
|
4803
4961
|
try {
|
|
4804
4962
|
const home = process.env.HOME || "";
|
|
4805
4963
|
const plugins = JSON.parse(
|
|
4806
|
-
|
|
4964
|
+
fs13.readFileSync(
|
|
4807
4965
|
`${home}/.claude/plugins/installed_plugins.json`,
|
|
4808
4966
|
"utf-8"
|
|
4809
4967
|
)
|
|
@@ -4835,7 +4993,7 @@ var doctorCommand = new Command("doctor").description("Check system health and p
|
|
|
4835
4993
|
const results = [];
|
|
4836
4994
|
results.push(check("Claude Code installed", commandExists("claude")));
|
|
4837
4995
|
results.push(check(`Claude Code version >= ${compatManifest.tools.claude.min}`, claudeVersionOk()));
|
|
4838
|
-
results.push(check("Obsidian installed", commandExists("obsidian") ||
|
|
4996
|
+
results.push(check("Obsidian installed", commandExists("obsidian") || fs13.existsSync("/Applications/Obsidian.app")));
|
|
4839
4997
|
results.push(check("Node.js >= 18", nodeVersionOk()));
|
|
4840
4998
|
results.push(
|
|
4841
4999
|
check(
|
|
@@ -4908,7 +5066,7 @@ var doctorCommand = new Command("doctor").description("Check system health and p
|
|
|
4908
5066
|
const emitter = projectionRegistry.get(name);
|
|
4909
5067
|
const [userDest] = emitter.destinations("user");
|
|
4910
5068
|
if (!userDest) continue;
|
|
4911
|
-
const dir =
|
|
5069
|
+
const dir = path16.dirname(userDest.path);
|
|
4912
5070
|
let status;
|
|
4913
5071
|
try {
|
|
4914
5072
|
await stat(dir);
|
|
@@ -4921,7 +5079,7 @@ var doctorCommand = new Command("doctor").description("Check system health and p
|
|
|
4921
5079
|
results.push(
|
|
4922
5080
|
check(
|
|
4923
5081
|
"Squadrant config exists",
|
|
4924
|
-
|
|
5082
|
+
fs13.existsSync(
|
|
4925
5083
|
process.env.SQUADRANT_CONFIG || `${process.env.HOME}/.config/squadrant/config.json`
|
|
4926
5084
|
)
|
|
4927
5085
|
)
|
|
@@ -4994,39 +5152,39 @@ init_dist();
|
|
|
4994
5152
|
init_dist3();
|
|
4995
5153
|
init_dist();
|
|
4996
5154
|
import { Command as Command2 } from "commander";
|
|
4997
|
-
import
|
|
4998
|
-
import
|
|
4999
|
-
import
|
|
5155
|
+
import fs14 from "fs";
|
|
5156
|
+
import path17 from "path";
|
|
5157
|
+
import os8 from "os";
|
|
5000
5158
|
import chalk4 from "chalk";
|
|
5001
5159
|
function findPackageRoot() {
|
|
5002
|
-
let dir =
|
|
5160
|
+
let dir = path17.dirname(new URL(import.meta.url).pathname);
|
|
5003
5161
|
while (dir !== "/") {
|
|
5004
|
-
if (
|
|
5005
|
-
dir =
|
|
5162
|
+
if (fs14.existsSync(path17.join(dir, "package.json"))) return dir;
|
|
5163
|
+
dir = path17.dirname(dir);
|
|
5006
5164
|
}
|
|
5007
5165
|
return process.cwd();
|
|
5008
5166
|
}
|
|
5009
5167
|
function copyDirRecursive(src, dest) {
|
|
5010
|
-
|
|
5011
|
-
for (const entry of
|
|
5012
|
-
const srcPath =
|
|
5013
|
-
const destPath =
|
|
5168
|
+
fs14.mkdirSync(dest, { recursive: true });
|
|
5169
|
+
for (const entry of fs14.readdirSync(src, { withFileTypes: true })) {
|
|
5170
|
+
const srcPath = path17.join(src, entry.name);
|
|
5171
|
+
const destPath = path17.join(dest, entry.name);
|
|
5014
5172
|
if (entry.isDirectory()) {
|
|
5015
5173
|
copyDirRecursive(srcPath, destPath);
|
|
5016
5174
|
} else {
|
|
5017
|
-
|
|
5175
|
+
fs14.copyFileSync(srcPath, destPath);
|
|
5018
5176
|
}
|
|
5019
5177
|
}
|
|
5020
5178
|
}
|
|
5021
5179
|
var initCommand = new Command2("init").description("First-time setup: scaffold hub vault, scripts, and config").option("--hub <path>", "Hub vault path", "~/squadrant-hub").action((opts) => {
|
|
5022
5180
|
const hubPath = resolveHome(opts.hub);
|
|
5023
5181
|
const pkgRoot = findPackageRoot();
|
|
5024
|
-
const configDir =
|
|
5182
|
+
const configDir = path17.join(os8.homedir(), ".config", "squadrant");
|
|
5025
5183
|
console.log(chalk4.bold("\nSquadrant Init\n"));
|
|
5026
5184
|
const registry = new WorkspaceRegistry({ obsidian: createObsidianDriver });
|
|
5027
5185
|
try {
|
|
5028
|
-
if (
|
|
5029
|
-
const existing = JSON.parse(
|
|
5186
|
+
if (fs14.existsSync(DEFAULT_CONFIG_PATH)) {
|
|
5187
|
+
const existing = JSON.parse(fs14.readFileSync(DEFAULT_CONFIG_PATH, "utf-8"));
|
|
5030
5188
|
const wsName = existing.workspace ?? "obsidian";
|
|
5031
5189
|
registry.get(wsName);
|
|
5032
5190
|
}
|
|
@@ -5034,7 +5192,7 @@ var initCommand = new Command2("init").description("First-time setup: scaffold h
|
|
|
5034
5192
|
console.log(chalk4.red(` \u2718 ${err.message}`));
|
|
5035
5193
|
return;
|
|
5036
5194
|
}
|
|
5037
|
-
if (
|
|
5195
|
+
if (fs14.existsSync(DEFAULT_CONFIG_PATH)) {
|
|
5038
5196
|
console.log(chalk4.yellow(" \u26A0 Config already exists, skipping creation"));
|
|
5039
5197
|
} else {
|
|
5040
5198
|
const config = getDefaultConfig();
|
|
@@ -5042,37 +5200,37 @@ var initCommand = new Command2("init").description("First-time setup: scaffold h
|
|
|
5042
5200
|
saveConfig(config);
|
|
5043
5201
|
console.log(chalk4.green(` \u2714 Config created at ${DEFAULT_CONFIG_PATH}`));
|
|
5044
5202
|
}
|
|
5045
|
-
const hubTemplate =
|
|
5046
|
-
if (
|
|
5203
|
+
const hubTemplate = path17.join(pkgRoot, "obsidian", "hub");
|
|
5204
|
+
if (fs14.existsSync(hubPath)) {
|
|
5047
5205
|
console.log(chalk4.yellow(` \u26A0 Hub vault already exists at ${hubPath}, skipping`));
|
|
5048
|
-
} else if (
|
|
5206
|
+
} else if (fs14.existsSync(hubTemplate)) {
|
|
5049
5207
|
copyDirRecursive(hubTemplate, hubPath);
|
|
5050
5208
|
console.log(chalk4.green(` \u2714 Hub vault scaffolded at ${hubPath}`));
|
|
5051
5209
|
} else {
|
|
5052
|
-
|
|
5210
|
+
fs14.mkdirSync(hubPath, { recursive: true });
|
|
5053
5211
|
console.log(chalk4.yellow(` \u26A0 Hub template not found; created empty dir at ${hubPath}`));
|
|
5054
5212
|
}
|
|
5055
|
-
const hubDashboardSrc =
|
|
5056
|
-
const hubDashboardDest =
|
|
5057
|
-
if (
|
|
5058
|
-
|
|
5213
|
+
const hubDashboardSrc = path17.join(pkgRoot, "obsidian", "hub", "dashboard.md");
|
|
5214
|
+
const hubDashboardDest = path17.join(hubPath, "dashboard.md");
|
|
5215
|
+
if (fs14.existsSync(hubDashboardSrc)) {
|
|
5216
|
+
fs14.copyFileSync(hubDashboardSrc, hubDashboardDest);
|
|
5059
5217
|
console.log(chalk4.green(` \u2714 Dashboard page refreshed at ${hubDashboardDest}`));
|
|
5060
5218
|
}
|
|
5061
|
-
const projectsDir =
|
|
5062
|
-
|
|
5219
|
+
const projectsDir = path17.join(hubPath, "projects");
|
|
5220
|
+
fs14.mkdirSync(projectsDir, { recursive: true });
|
|
5063
5221
|
ensureRuntimeSynced({ sourceRoot: pkgRoot, runtimeRoot: configDir });
|
|
5064
5222
|
console.log(chalk4.green(` \u2714 Runtime assets synced to ${configDir}`));
|
|
5065
|
-
const settingsPath =
|
|
5223
|
+
const settingsPath = path17.join(os8.homedir(), ".claude", "settings.json");
|
|
5066
5224
|
try {
|
|
5067
5225
|
let settings = {};
|
|
5068
|
-
if (
|
|
5069
|
-
settings = JSON.parse(
|
|
5226
|
+
if (fs14.existsSync(settingsPath)) {
|
|
5227
|
+
settings = JSON.parse(fs14.readFileSync(settingsPath, "utf-8"));
|
|
5070
5228
|
}
|
|
5071
5229
|
const env = settings.env || {};
|
|
5072
5230
|
if (env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS !== "1") {
|
|
5073
5231
|
settings.env = { ...env, CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" };
|
|
5074
|
-
|
|
5075
|
-
|
|
5232
|
+
fs14.mkdirSync(path17.dirname(settingsPath), { recursive: true });
|
|
5233
|
+
fs14.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
5076
5234
|
console.log(chalk4.green(" \u2714 Agent Teams enabled in ~/.claude/settings.json"));
|
|
5077
5235
|
} else {
|
|
5078
5236
|
console.log(chalk4.green(" \u2714 Agent Teams already enabled"));
|
|
@@ -5093,7 +5251,7 @@ var initCommand = new Command2("init").description("First-time setup: scaffold h
|
|
|
5093
5251
|
console.log(chalk4.cyan(` ${hubPath}`));
|
|
5094
5252
|
console.log("");
|
|
5095
5253
|
console.log(" 4. Run " + chalk4.cyan("squadrant doctor") + " to verify setup\n");
|
|
5096
|
-
if (!
|
|
5254
|
+
if (!fs14.existsSync("/Applications/cmux.app")) {
|
|
5097
5255
|
console.log(chalk4.yellow(" \u26A0 cmux not found \u2014 download from https://cmux.dev\n"));
|
|
5098
5256
|
}
|
|
5099
5257
|
});
|
|
@@ -5101,26 +5259,70 @@ var initCommand = new Command2("init").description("First-time setup: scaffold h
|
|
|
5101
5259
|
// packages/cli/src/commands/projects.ts
|
|
5102
5260
|
init_dist();
|
|
5103
5261
|
import { Command as Command3 } from "commander";
|
|
5104
|
-
import
|
|
5105
|
-
import
|
|
5262
|
+
import fs15 from "fs";
|
|
5263
|
+
import path18 from "path";
|
|
5106
5264
|
import chalk5 from "chalk";
|
|
5265
|
+
|
|
5266
|
+
// packages/cli/src/control/restart-daemon.ts
|
|
5267
|
+
init_dist2();
|
|
5268
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
5269
|
+
import { existsSync as existsSync9 } from "fs";
|
|
5270
|
+
import { homedir as homedir9 } from "os";
|
|
5271
|
+
import { join as join14 } from "path";
|
|
5272
|
+
var DEFAULT_SOCK_PATH = join14(homedir9(), ".config", "squadrant", "squadrant.sock");
|
|
5273
|
+
function defaultIsRunning() {
|
|
5274
|
+
return existsSync9(DEFAULT_SOCK_PATH);
|
|
5275
|
+
}
|
|
5276
|
+
function defaultRunKickstart() {
|
|
5277
|
+
const uid = process.getuid?.() ?? 0;
|
|
5278
|
+
const target = `gui/${uid}/${LABEL}`;
|
|
5279
|
+
if (tryAcquireDaemonLock()) {
|
|
5280
|
+
try {
|
|
5281
|
+
execFileSync6("launchctl", kickstartArgv(target, true), { stdio: "ignore" });
|
|
5282
|
+
} finally {
|
|
5283
|
+
releaseDaemonLock();
|
|
5284
|
+
}
|
|
5285
|
+
}
|
|
5286
|
+
}
|
|
5287
|
+
function restartDaemonIfRunning(opts) {
|
|
5288
|
+
const env = opts.env ?? process.env;
|
|
5289
|
+
if (env["VITEST"] || opts.noRestart) return "skipped-opt-out";
|
|
5290
|
+
const isRunning = opts.isRunning ?? defaultIsRunning;
|
|
5291
|
+
if (!isRunning()) return "skipped-not-running";
|
|
5292
|
+
const log = opts.log ?? console.log;
|
|
5293
|
+
log(`\u21BB restarting daemon to apply ${opts.reason}\u2026`);
|
|
5294
|
+
const runKickstart = opts.runKickstart ?? defaultRunKickstart;
|
|
5295
|
+
runKickstart();
|
|
5296
|
+
return "restarted";
|
|
5297
|
+
}
|
|
5298
|
+
|
|
5299
|
+
// packages/cli/src/commands/projects.ts
|
|
5300
|
+
function restartAfterProjectsAdd(opts) {
|
|
5301
|
+
const doRestart = opts.doRestart ?? restartDaemonIfRunning;
|
|
5302
|
+
const outcome = doRestart({ reason: "project registration", noRestart: opts.noRestart });
|
|
5303
|
+
if (outcome === "skipped-not-running") {
|
|
5304
|
+
console.log(chalk5.dim(" (daemon not running \u2014 change applies on next start)"));
|
|
5305
|
+
} else if (outcome === "skipped-opt-out") {
|
|
5306
|
+
console.log(chalk5.dim(" (run 'squadrant heal daemon' to apply)"));
|
|
5307
|
+
}
|
|
5308
|
+
}
|
|
5107
5309
|
function findPackageRoot2() {
|
|
5108
|
-
let dir =
|
|
5310
|
+
let dir = path18.dirname(new URL(import.meta.url).pathname);
|
|
5109
5311
|
while (dir !== "/") {
|
|
5110
|
-
if (
|
|
5111
|
-
dir =
|
|
5312
|
+
if (fs15.existsSync(path18.join(dir, "package.json"))) return dir;
|
|
5313
|
+
dir = path18.dirname(dir);
|
|
5112
5314
|
}
|
|
5113
5315
|
return process.cwd();
|
|
5114
5316
|
}
|
|
5115
5317
|
function copyDirRecursive2(src, dest) {
|
|
5116
|
-
|
|
5117
|
-
for (const entry of
|
|
5118
|
-
const srcPath =
|
|
5119
|
-
const destPath =
|
|
5318
|
+
fs15.mkdirSync(dest, { recursive: true });
|
|
5319
|
+
for (const entry of fs15.readdirSync(src, { withFileTypes: true })) {
|
|
5320
|
+
const srcPath = path18.join(src, entry.name);
|
|
5321
|
+
const destPath = path18.join(dest, entry.name);
|
|
5120
5322
|
if (entry.isDirectory()) {
|
|
5121
5323
|
copyDirRecursive2(srcPath, destPath);
|
|
5122
5324
|
} else {
|
|
5123
|
-
|
|
5325
|
+
fs15.copyFileSync(srcPath, destPath);
|
|
5124
5326
|
}
|
|
5125
5327
|
}
|
|
5126
5328
|
}
|
|
@@ -5147,7 +5349,7 @@ var listCmd = new Command3("list").description("List registered projects").actio
|
|
|
5147
5349
|
}
|
|
5148
5350
|
console.log("");
|
|
5149
5351
|
});
|
|
5150
|
-
var addCmd = new Command3("add").description("Register a project").argument("<name>", "Project name").argument("<path>", "Path to project directory").option("--captain <name>", "Captain workspace name (default: <project>-captain)").option("--spoke <path>", "Spoke vault path (default: ~/squadrant-hub/spokes/<name>)").option("--group <name>", "Project group name (siblings share context)").option("--group-role <role>", "Role within the group (auto-set to 'primary' if first in group)").action((name, projectPath, opts) => {
|
|
5352
|
+
var addCmd = new Command3("add").description("Register a project").argument("<name>", "Project name").argument("<path>", "Path to project directory").option("--captain <name>", "Captain workspace name (default: <project>-captain)").option("--spoke <path>", "Spoke vault path (default: ~/squadrant-hub/spokes/<name>)").option("--group <name>", "Project group name (siblings share context)").option("--group-role <role>", "Role within the group (auto-set to 'primary' if first in group)").option("--no-restart", "skip daemon restart after registration").action((name, projectPath, opts) => {
|
|
5151
5353
|
const config = loadConfig();
|
|
5152
5354
|
if (config.projects[name]) {
|
|
5153
5355
|
console.log(chalk5.yellow(`
|
|
@@ -5156,7 +5358,7 @@ var addCmd = new Command3("add").description("Register a project").argument("<na
|
|
|
5156
5358
|
process.exit(1);
|
|
5157
5359
|
}
|
|
5158
5360
|
const resolvedPath = resolveHome(projectPath);
|
|
5159
|
-
if (!
|
|
5361
|
+
if (!fs15.existsSync(path18.join(resolvedPath, ".git"))) {
|
|
5160
5362
|
console.log(chalk5.yellow(`
|
|
5161
5363
|
\u26A0 No .git found at ${resolvedPath}. Make sure this is the project root, not a parent directory.
|
|
5162
5364
|
`));
|
|
@@ -5209,7 +5411,7 @@ var addCmd = new Command3("add").description("Register a project").argument("<na
|
|
|
5209
5411
|
\u26A0 Group '${group}' already has '${primary[0]}' as primary. Overriding.`));
|
|
5210
5412
|
}
|
|
5211
5413
|
}
|
|
5212
|
-
const spokeVault = opts.spoke ? resolveHome(opts.spoke) :
|
|
5414
|
+
const spokeVault = opts.spoke ? resolveHome(opts.spoke) : path18.join(config.hubVault, "spokes", name);
|
|
5213
5415
|
const project = {
|
|
5214
5416
|
path: resolvedPath,
|
|
5215
5417
|
captainName,
|
|
@@ -5222,21 +5424,22 @@ var addCmd = new Command3("add").description("Register a project").argument("<na
|
|
|
5222
5424
|
saveConfig(config);
|
|
5223
5425
|
console.log(chalk5.green(`
|
|
5224
5426
|
\u2714 Project '${name}' registered`));
|
|
5427
|
+
restartAfterProjectsAdd({ noRestart: opts.restart === false });
|
|
5225
5428
|
const pkgRoot = findPackageRoot2();
|
|
5226
|
-
const spokeTemplate =
|
|
5227
|
-
if (
|
|
5429
|
+
const spokeTemplate = path18.join(pkgRoot, "obsidian", "spoke");
|
|
5430
|
+
if (fs15.existsSync(spokeVault)) {
|
|
5228
5431
|
console.log(chalk5.yellow(` \u26A0 Spoke vault already exists at ${spokeVault}, skipping scaffold`));
|
|
5229
|
-
} else if (
|
|
5432
|
+
} else if (fs15.existsSync(spokeTemplate)) {
|
|
5230
5433
|
copyDirRecursive2(spokeTemplate, spokeVault);
|
|
5231
|
-
const statusPath =
|
|
5232
|
-
if (
|
|
5233
|
-
const content =
|
|
5434
|
+
const statusPath = path18.join(spokeVault, "status.md");
|
|
5435
|
+
if (fs15.existsSync(statusPath)) {
|
|
5436
|
+
const content = fs15.readFileSync(statusPath, "utf-8");
|
|
5234
5437
|
const updated = content.replace(/^project: unnamed/m, `project: ${name}`);
|
|
5235
|
-
|
|
5438
|
+
fs15.writeFileSync(statusPath, updated);
|
|
5236
5439
|
}
|
|
5237
5440
|
console.log(chalk5.green(` \u2714 Spoke vault scaffolded at ${spokeVault}`));
|
|
5238
5441
|
} else {
|
|
5239
|
-
|
|
5442
|
+
fs15.mkdirSync(spokeVault, { recursive: true });
|
|
5240
5443
|
console.log(chalk5.yellow(` \u26A0 Spoke template not found; created empty dir at ${spokeVault}`));
|
|
5241
5444
|
}
|
|
5242
5445
|
console.log("");
|
|
@@ -5337,10 +5540,10 @@ init_dist4();
|
|
|
5337
5540
|
init_dist();
|
|
5338
5541
|
import { Command as Command5 } from "commander";
|
|
5339
5542
|
import { execSync as execSync9 } from "child_process";
|
|
5340
|
-
import
|
|
5341
|
-
import
|
|
5543
|
+
import path19 from "path";
|
|
5544
|
+
import os9 from "os";
|
|
5342
5545
|
import chalk7 from "chalk";
|
|
5343
|
-
var TEMPLATES_DIR =
|
|
5546
|
+
var TEMPLATES_DIR = path19.join(os9.homedir(), ".config", "squadrant", "templates");
|
|
5344
5547
|
var TASK_PROMPTS = {
|
|
5345
5548
|
briefing: "Run your daily briefing using the squadrant:command-ops skill. Read all spoke handoffs, yesterday's logs, current status; produce a concise cross-project briefing; save to {hubVault}/daily-logs/YYYY-MM-DD.md; then exit.",
|
|
5346
5549
|
"learnings-review": "Run a learnings review using the squadrant:command-ops skill. Scan {spokeVault}/learnings across all projects, identify cross-project patterns, propose captured-skill or fix actions, and exit when done.",
|
|
@@ -5373,7 +5576,7 @@ async function runCommandSpawn(input) {
|
|
|
5373
5576
|
if (!agent) {
|
|
5374
5577
|
throw new Error(`Unknown agent '${agentName}'. Known: claude, codex, gemini, opencode.`);
|
|
5375
5578
|
}
|
|
5376
|
-
const promptFile =
|
|
5579
|
+
const promptFile = path19.join(TEMPLATES_DIR, `command.${agent.templateSuffix}.md`);
|
|
5377
5580
|
const cliCommand = agent.buildCommand({
|
|
5378
5581
|
prompt,
|
|
5379
5582
|
workdir: process.cwd(),
|
|
@@ -5400,9 +5603,9 @@ var commandCommand = new Command5("command").description("Spawn a one-shot Comma
|
|
|
5400
5603
|
init_dist();
|
|
5401
5604
|
init_dist();
|
|
5402
5605
|
import { Command as Command9 } from "commander";
|
|
5403
|
-
import
|
|
5404
|
-
import
|
|
5405
|
-
import
|
|
5606
|
+
import fs16 from "fs";
|
|
5607
|
+
import path20 from "path";
|
|
5608
|
+
import os10 from "os";
|
|
5406
5609
|
import chalk9 from "chalk";
|
|
5407
5610
|
|
|
5408
5611
|
// packages/cli/src/control/crew-routing.ts
|
|
@@ -5435,8 +5638,8 @@ init_dist4();
|
|
|
5435
5638
|
import { Command as Command8 } from "commander";
|
|
5436
5639
|
import { createConnection as createConnection3 } from "net";
|
|
5437
5640
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5438
|
-
import { homedir as
|
|
5439
|
-
import { join as
|
|
5641
|
+
import { homedir as homedir11 } from "os";
|
|
5642
|
+
import { join as join16 } from "path";
|
|
5440
5643
|
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
5441
5644
|
|
|
5442
5645
|
// packages/cli/src/commands/crew-output.ts
|
|
@@ -5495,11 +5698,11 @@ init_dist2();
|
|
|
5495
5698
|
import { Command as Command6 } from "commander";
|
|
5496
5699
|
import chalk8 from "chalk";
|
|
5497
5700
|
import { createConnection as createConnection2 } from "net";
|
|
5498
|
-
import { homedir as
|
|
5499
|
-
import { join as
|
|
5701
|
+
import { homedir as homedir10 } from "os";
|
|
5702
|
+
import { join as join15 } from "path";
|
|
5500
5703
|
import { createInterface } from "readline";
|
|
5501
5704
|
function socketPath() {
|
|
5502
|
-
return process.env.SQUADRANTD_SOCK ??
|
|
5705
|
+
return process.env.SQUADRANTD_SOCK ?? join15(homedir10(), ".config", "squadrant", "squadrant.sock");
|
|
5503
5706
|
}
|
|
5504
5707
|
function rule(width, ch = "\u2500") {
|
|
5505
5708
|
return ch.repeat(Math.max(0, width));
|
|
@@ -5735,7 +5938,7 @@ var crewChatCommand = new Command7("chat").description("[DEPRECATED] alias for `
|
|
|
5735
5938
|
});
|
|
5736
5939
|
|
|
5737
5940
|
// packages/cli/src/commands/crew-control.ts
|
|
5738
|
-
var SOCK2 =
|
|
5941
|
+
var SOCK2 = join16(homedir11(), ".config", "squadrant", "squadrant.sock");
|
|
5739
5942
|
var CODEX_FIRST_TURN_DELAY_MS = 1500;
|
|
5740
5943
|
async function sendCodexFirstTurn(taskId, text) {
|
|
5741
5944
|
await new Promise((r) => setTimeout(r, CODEX_FIRST_TURN_DELAY_MS));
|
|
@@ -5840,9 +6043,9 @@ function buildSignalRequest(signal, o) {
|
|
|
5840
6043
|
return { kind: "event", project, event };
|
|
5841
6044
|
}
|
|
5842
6045
|
function defaultWriteResult(id, payload) {
|
|
5843
|
-
const dir =
|
|
6046
|
+
const dir = join16(homedir11(), ".config", "squadrant", "state", "_results");
|
|
5844
6047
|
mkdirSync6(dir, { recursive: true });
|
|
5845
|
-
const file =
|
|
6048
|
+
const file = join16(dir, `${id}.txt`);
|
|
5846
6049
|
writeFileSync7(file, payload);
|
|
5847
6050
|
return file;
|
|
5848
6051
|
}
|
|
@@ -5943,7 +6146,7 @@ addControlPlaneCrewCommands(crewControlCommand);
|
|
|
5943
6146
|
// packages/cli/src/lib/per-crew-settings.ts
|
|
5944
6147
|
init_dist4();
|
|
5945
6148
|
import { mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
|
|
5946
|
-
import { join as
|
|
6149
|
+
import { join as join17 } from "path";
|
|
5947
6150
|
var CREW_PERMISSION_ALLOWLIST = [
|
|
5948
6151
|
// git — read + safe mutations (reset/clean/config intentionally excluded)
|
|
5949
6152
|
"Bash(git status:*)",
|
|
@@ -6031,9 +6234,9 @@ function mergeCrewPermissions(settings) {
|
|
|
6031
6234
|
return next;
|
|
6032
6235
|
}
|
|
6033
6236
|
function writePerCrewSettingsLocal(o) {
|
|
6034
|
-
const dir =
|
|
6237
|
+
const dir = join17(o.projectCwd, ".claude");
|
|
6035
6238
|
mkdirSync7(dir, { recursive: true });
|
|
6036
|
-
const file =
|
|
6239
|
+
const file = join17(dir, "settings.local.json");
|
|
6037
6240
|
let existing = {};
|
|
6038
6241
|
try {
|
|
6039
6242
|
const raw = readFileSync9(file, "utf-8");
|
|
@@ -6046,9 +6249,9 @@ function writePerCrewSettingsLocal(o) {
|
|
|
6046
6249
|
return file;
|
|
6047
6250
|
}
|
|
6048
6251
|
function writePerCrewOpencodeConfig(o) {
|
|
6049
|
-
const dir =
|
|
6252
|
+
const dir = join17(o.stateRoot, o.project, o.taskId);
|
|
6050
6253
|
mkdirSync7(dir, { recursive: true });
|
|
6051
|
-
const file =
|
|
6254
|
+
const file = join17(dir, "opencode.json");
|
|
6052
6255
|
const config = {
|
|
6053
6256
|
permission: {
|
|
6054
6257
|
read: "allow",
|
|
@@ -6069,7 +6272,7 @@ function writePerCrewOpencodeConfig(o) {
|
|
|
6069
6272
|
|
|
6070
6273
|
// packages/cli/src/commands/crew.ts
|
|
6071
6274
|
init_dist2();
|
|
6072
|
-
var TEMPLATES_DIR2 =
|
|
6275
|
+
var TEMPLATES_DIR2 = path20.join(os10.homedir(), ".config", "squadrant", "templates");
|
|
6073
6276
|
async function runCrewSpawn(input) {
|
|
6074
6277
|
const config = loadConfig();
|
|
6075
6278
|
const proj = config.projects[input.project];
|
|
@@ -6117,8 +6320,8 @@ async function runCrewSpawn(input) {
|
|
|
6117
6320
|
throw new Error(`Unknown agent '${agentName}'. Known: claude, codex, gemini, opencode.`);
|
|
6118
6321
|
}
|
|
6119
6322
|
if (agentName === "codex") {
|
|
6120
|
-
const codexRoleFile =
|
|
6121
|
-
const roleInstructions =
|
|
6323
|
+
const codexRoleFile = path20.join(TEMPLATES_DIR2, `crew.${agent.templateSuffix}.md`);
|
|
6324
|
+
const roleInstructions = fs16.existsSync(codexRoleFile) ? fs16.readFileSync(codexRoleFile, "utf8") : void 0;
|
|
6122
6325
|
return runCodexInteractiveSpawn({
|
|
6123
6326
|
project: input.project,
|
|
6124
6327
|
task: input.task,
|
|
@@ -6131,7 +6334,7 @@ async function runCrewSpawn(input) {
|
|
|
6131
6334
|
roleInstructions
|
|
6132
6335
|
});
|
|
6133
6336
|
}
|
|
6134
|
-
const promptFile =
|
|
6337
|
+
const promptFile = path20.join(TEMPLATES_DIR2, `crew.${agent.templateSuffix}.md`);
|
|
6135
6338
|
const interactive = agent.name === "claude" || agent.name === "opencode";
|
|
6136
6339
|
const crewRole = config.defaults.roles?.crew;
|
|
6137
6340
|
const configModel = crewRole && crewRole.agent === agent.name ? crewRole.model : void 0;
|
|
@@ -6188,7 +6391,7 @@ ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
|
|
|
6188
6391
|
});
|
|
6189
6392
|
const rec = await squadrantdCall(req);
|
|
6190
6393
|
const opencodeConfigPath = writePerCrewOpencodeConfig({
|
|
6191
|
-
stateRoot:
|
|
6394
|
+
stateRoot: path20.join(os10.homedir(), ".config", "squadrant", "state"),
|
|
6192
6395
|
project: input.project,
|
|
6193
6396
|
taskId: rec.id,
|
|
6194
6397
|
// CP3 opt-in: --approval gates bash so the captain approves shell commands.
|
|
@@ -6428,11 +6631,11 @@ init_dist3();
|
|
|
6428
6631
|
init_dist();
|
|
6429
6632
|
init_dist();
|
|
6430
6633
|
import { Command as Command10 } from "commander";
|
|
6431
|
-
import
|
|
6432
|
-
import
|
|
6433
|
-
import
|
|
6634
|
+
import fs17 from "fs";
|
|
6635
|
+
import path21 from "path";
|
|
6636
|
+
import os11 from "os";
|
|
6434
6637
|
import chalk10 from "chalk";
|
|
6435
|
-
var TEMPLATES_DIR3 =
|
|
6638
|
+
var TEMPLATES_DIR3 = path21.join(os11.homedir(), ".config", "squadrant", "templates");
|
|
6436
6639
|
var SIDE_ROLES = ["research", "debug"];
|
|
6437
6640
|
function shellQuote2(p) {
|
|
6438
6641
|
return "'" + p.replace(/'/g, "'\\''") + "'";
|
|
@@ -6523,7 +6726,7 @@ async function runSideSpawn(input) {
|
|
|
6523
6726
|
throw new Error(`Unknown agent '${agentName}'. Known: claude, codex, gemini, opencode.`);
|
|
6524
6727
|
}
|
|
6525
6728
|
const sideModel = sideRole?.model;
|
|
6526
|
-
const promptFile =
|
|
6729
|
+
const promptFile = path21.join(
|
|
6527
6730
|
TEMPLATES_DIR3,
|
|
6528
6731
|
`side.${input.role}.${agent.templateSuffix}.md`
|
|
6529
6732
|
);
|
|
@@ -6534,7 +6737,7 @@ async function runSideSpawn(input) {
|
|
|
6534
6737
|
prompt: input.topic,
|
|
6535
6738
|
workdir: spawnCwd,
|
|
6536
6739
|
role: "side",
|
|
6537
|
-
promptFile:
|
|
6740
|
+
promptFile: fs17.existsSync(promptFile) ? promptFile : void 0,
|
|
6538
6741
|
interactive: true,
|
|
6539
6742
|
permissionMode: config.defaults.permissions?.crew ?? "auto",
|
|
6540
6743
|
...sideModel ? { model: sideModel } : {}
|
|
@@ -6591,7 +6794,7 @@ async function runSideClose(project, name) {
|
|
|
6591
6794
|
project,
|
|
6592
6795
|
name
|
|
6593
6796
|
);
|
|
6594
|
-
if (
|
|
6797
|
+
if (fs17.existsSync(wtPath)) {
|
|
6595
6798
|
try {
|
|
6596
6799
|
removeWorktree(proj.path, wtPath);
|
|
6597
6800
|
} catch (e) {
|
|
@@ -6679,8 +6882,8 @@ init_dist();
|
|
|
6679
6882
|
init_dist3();
|
|
6680
6883
|
import { Command as Command11 } from "commander";
|
|
6681
6884
|
import { execSync as execSync10 } from "child_process";
|
|
6682
|
-
import { homedir as
|
|
6683
|
-
import { join as
|
|
6885
|
+
import { homedir as homedir13 } from "os";
|
|
6886
|
+
import { join as join19 } from "path";
|
|
6684
6887
|
import chalk12 from "chalk";
|
|
6685
6888
|
|
|
6686
6889
|
// packages/web/dist/read-status.js
|
|
@@ -6819,8 +7022,8 @@ function renderDashboard(rows, opts) {
|
|
|
6819
7022
|
|
|
6820
7023
|
// packages/web/dist/sync-hub.js
|
|
6821
7024
|
init_dist();
|
|
6822
|
-
import
|
|
6823
|
-
import
|
|
7025
|
+
import fs18 from "fs";
|
|
7026
|
+
import path22 from "path";
|
|
6824
7027
|
function buildMirrorMarkdown(s) {
|
|
6825
7028
|
const fenced = "```";
|
|
6826
7029
|
return [
|
|
@@ -6846,15 +7049,15 @@ function buildMirrorMarkdown(s) {
|
|
|
6846
7049
|
function syncHub(deps) {
|
|
6847
7050
|
if (!deps.config.hubVault)
|
|
6848
7051
|
return [];
|
|
6849
|
-
const writeFile5 = deps.writeFile ?? ((p, c) =>
|
|
6850
|
-
const mkdir5 = deps.mkdir ?? ((p) =>
|
|
6851
|
-
const projectsDir =
|
|
7052
|
+
const writeFile5 = deps.writeFile ?? ((p, c) => fs18.writeFileSync(p, c));
|
|
7053
|
+
const mkdir5 = deps.mkdir ?? ((p) => fs18.mkdirSync(p, { recursive: true }));
|
|
7054
|
+
const projectsDir = path22.join(resolveHome(deps.config.hubVault), "projects");
|
|
6852
7055
|
mkdir5(projectsDir);
|
|
6853
7056
|
const out = [];
|
|
6854
7057
|
for (const s of deps.statuses) {
|
|
6855
7058
|
if (s.state === "unknown")
|
|
6856
7059
|
continue;
|
|
6857
|
-
const hubPath =
|
|
7060
|
+
const hubPath = path22.join(projectsDir, `${s.project}.md`);
|
|
6858
7061
|
try {
|
|
6859
7062
|
writeFile5(hubPath, buildMirrorMarkdown(s));
|
|
6860
7063
|
out.push({ project: s.project, hubPath });
|
|
@@ -6876,9 +7079,9 @@ function mergeSnapshot(daemon, external, now) {
|
|
|
6876
7079
|
// packages/web/dist/probes.js
|
|
6877
7080
|
init_dist();
|
|
6878
7081
|
init_dist();
|
|
6879
|
-
import { join as
|
|
6880
|
-
import { homedir as
|
|
6881
|
-
import { existsSync as
|
|
7082
|
+
import { join as join18 } from "path";
|
|
7083
|
+
import { homedir as homedir12 } from "os";
|
|
7084
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
6882
7085
|
import { execFile as execFile2 } from "child_process";
|
|
6883
7086
|
var DEFAULT_TIMEOUT_MS = 2e3;
|
|
6884
7087
|
var AGENT_CLIS = ["claude", "codex", "gemini", "opencode"];
|
|
@@ -6914,7 +7117,7 @@ function vaultProbe(run, dir) {
|
|
|
6914
7117
|
return { state: "unknown", detail: "no vault configured" };
|
|
6915
7118
|
if (!run.pathExists(dir))
|
|
6916
7119
|
return { state: "gone", detail: "vault directory missing" };
|
|
6917
|
-
if (!run.pathExists(
|
|
7120
|
+
if (!run.pathExists(join18(dir, ".obsidian")))
|
|
6918
7121
|
return { state: "gone", detail: "no .obsidian/ (not a vault)" };
|
|
6919
7122
|
return { state: "alive" };
|
|
6920
7123
|
} catch {
|
|
@@ -6982,10 +7185,10 @@ async function runExternalProbes(run, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
6982
7185
|
const sessions = probeSessions(run);
|
|
6983
7186
|
return { cmux: cmux2, agentClis, vaults, config: { parseable, projectPaths, sessions } };
|
|
6984
7187
|
}
|
|
6985
|
-
var SESSIONS_PATH =
|
|
7188
|
+
var SESSIONS_PATH = join18(homedir12(), ".config", "squadrant", "sessions.json");
|
|
6986
7189
|
function onPath(cli) {
|
|
6987
7190
|
const dirs = (process.env.PATH ?? "").split(":").filter(Boolean);
|
|
6988
|
-
return dirs.some((d) =>
|
|
7191
|
+
return dirs.some((d) => existsSync10(join18(d, cli)));
|
|
6989
7192
|
}
|
|
6990
7193
|
function readSessionsHashes() {
|
|
6991
7194
|
const raw = JSON.parse(readFileSync10(SESSIONS_PATH, "utf-8"));
|
|
@@ -7002,7 +7205,7 @@ function defaultProbeRunners() {
|
|
|
7002
7205
|
}
|
|
7003
7206
|
}),
|
|
7004
7207
|
probeOnPath: async (cli) => onPath(cli),
|
|
7005
|
-
pathExists: (p) =>
|
|
7208
|
+
pathExists: (p) => existsSync10(p),
|
|
7006
7209
|
loadConfig: () => loadConfig(),
|
|
7007
7210
|
loadSessionsHashes: () => readSessionsHashes()
|
|
7008
7211
|
};
|
|
@@ -7655,7 +7858,7 @@ async function startWebServer(opts) {
|
|
|
7655
7858
|
|
|
7656
7859
|
// packages/cli/src/commands/dashboard.ts
|
|
7657
7860
|
init_dist();
|
|
7658
|
-
var SOCK3 =
|
|
7861
|
+
var SOCK3 = join19(homedir13(), ".config", "squadrant", "squadrant.sock");
|
|
7659
7862
|
function detectCurrentWorkspace2() {
|
|
7660
7863
|
const out = execSync10(`"${resolveCmuxBin()}" current-workspace`, { encoding: "utf-8" }).trim();
|
|
7661
7864
|
const match = out.match(/workspace:\d+/);
|
|
@@ -7743,13 +7946,13 @@ init_dist3();
|
|
|
7743
7946
|
init_dist2();
|
|
7744
7947
|
import { Command as Command12 } from "commander";
|
|
7745
7948
|
import { execSync as execSync11 } from "child_process";
|
|
7746
|
-
import
|
|
7747
|
-
import
|
|
7748
|
-
import
|
|
7949
|
+
import fs19 from "fs";
|
|
7950
|
+
import path23 from "path";
|
|
7951
|
+
import os12 from "os";
|
|
7749
7952
|
import chalk13 from "chalk";
|
|
7750
7953
|
var CMUX_APP = "/Applications/cmux.app";
|
|
7751
|
-
var TEMPLATES_DIR4 =
|
|
7752
|
-
var SESSIONS_PATH2 =
|
|
7954
|
+
var TEMPLATES_DIR4 = path23.join(os12.homedir(), ".config", "squadrant", "templates");
|
|
7955
|
+
var SESSIONS_PATH2 = path23.join(os12.homedir(), ".config", "squadrant", "sessions.json");
|
|
7753
7956
|
function ensureCmuxReady() {
|
|
7754
7957
|
if (isInsideCmux()) return;
|
|
7755
7958
|
console.log(chalk13.yellow("\n Not running inside cmux. Opening cmux app...\n"));
|
|
@@ -7856,12 +8059,12 @@ var launchCommand = new Command12("launch").description(
|
|
|
7856
8059
|
}
|
|
7857
8060
|
if (opts.all) {
|
|
7858
8061
|
const hubPath = resolveHome(config.hubVault);
|
|
7859
|
-
|
|
8062
|
+
fs19.mkdirSync(hubPath, { recursive: true });
|
|
7860
8063
|
console.log(chalk13.bold("\nLaunching all captain workspaces\n"));
|
|
7861
8064
|
for (const [name, proj] of Object.entries(config.projects)) {
|
|
7862
8065
|
const projPath = resolveHome(proj.path);
|
|
7863
8066
|
const spokePath = resolveHome(proj.spokeVault);
|
|
7864
|
-
if (!
|
|
8067
|
+
if (!fs19.existsSync(spokePath)) {
|
|
7865
8068
|
const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
|
|
7866
8069
|
await ensureSpokeLayout(spokeDriver);
|
|
7867
8070
|
console.log(chalk13.cyan(` \u2714 Created spoke vault at ${spokePath}`));
|
|
@@ -7892,7 +8095,7 @@ var launchCommand = new Command12("launch").description(
|
|
|
7892
8095
|
const proj = config.projects[project];
|
|
7893
8096
|
const projPath = resolveHome(proj.path);
|
|
7894
8097
|
const spokePath = resolveHome(proj.spokeVault);
|
|
7895
|
-
if (!
|
|
8098
|
+
if (!fs19.existsSync(spokePath)) {
|
|
7896
8099
|
const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(project, config);
|
|
7897
8100
|
await ensureSpokeLayout(spokeDriver);
|
|
7898
8101
|
console.log(chalk13.cyan(` \u2714 Created spoke vault at ${spokePath}`));
|
|
@@ -8027,24 +8230,24 @@ Shutting down captain workspace for '${project}'...
|
|
|
8027
8230
|
// packages/cli/src/commands/feedback.ts
|
|
8028
8231
|
init_dist();
|
|
8029
8232
|
import { Command as Command14 } from "commander";
|
|
8030
|
-
import
|
|
8031
|
-
import
|
|
8032
|
-
import
|
|
8233
|
+
import fs20 from "fs";
|
|
8234
|
+
import os13 from "os";
|
|
8235
|
+
import path24 from "path";
|
|
8033
8236
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8034
8237
|
import { execSync as execSync12 } from "child_process";
|
|
8035
8238
|
import chalk15 from "chalk";
|
|
8036
8239
|
var REPO_URL = "https://github.com/tu11aa/squadrant";
|
|
8037
8240
|
function readPkgVersion() {
|
|
8038
8241
|
try {
|
|
8039
|
-
const pkgPath =
|
|
8040
|
-
return JSON.parse(
|
|
8242
|
+
const pkgPath = path24.join(path24.dirname(fileURLToPath3(import.meta.url)), "..", "package.json");
|
|
8243
|
+
return JSON.parse(fs20.readFileSync(pkgPath, "utf-8")).version ?? "unknown";
|
|
8041
8244
|
} catch {
|
|
8042
8245
|
return "unknown";
|
|
8043
8246
|
}
|
|
8044
8247
|
}
|
|
8045
8248
|
function readMetrics(metricsPath) {
|
|
8046
8249
|
try {
|
|
8047
|
-
return JSON.parse(
|
|
8250
|
+
return JSON.parse(fs20.readFileSync(metricsPath, "utf-8"));
|
|
8048
8251
|
} catch {
|
|
8049
8252
|
return {};
|
|
8050
8253
|
}
|
|
@@ -8082,7 +8285,7 @@ function buildIssueUrl(metrics, squadrantVersion) {
|
|
|
8082
8285
|
}
|
|
8083
8286
|
var feedbackCommand = new Command14("feedback").description("Open a pre-filled GitHub issue for feedback or bug reports").action(() => {
|
|
8084
8287
|
const config = loadConfig();
|
|
8085
|
-
const metricsPath = config.metrics?.path ||
|
|
8288
|
+
const metricsPath = config.metrics?.path || path24.join(os13.homedir(), ".config", "squadrant", "metrics.json");
|
|
8086
8289
|
const metrics = readMetrics(metricsPath);
|
|
8087
8290
|
const version = readStamp(config) ?? readPkgVersion();
|
|
8088
8291
|
const issueUrl = buildIssueUrl(metrics, version);
|
|
@@ -8104,8 +8307,8 @@ init_dist();
|
|
|
8104
8307
|
init_dist();
|
|
8105
8308
|
init_dist3();
|
|
8106
8309
|
import { Command as Command15 } from "commander";
|
|
8107
|
-
import
|
|
8108
|
-
import
|
|
8310
|
+
import fs21 from "fs";
|
|
8311
|
+
import path25 from "path";
|
|
8109
8312
|
import chalk16 from "chalk";
|
|
8110
8313
|
import matter3 from "gray-matter";
|
|
8111
8314
|
function getDateStr(yesterday) {
|
|
@@ -8114,11 +8317,11 @@ function getDateStr(yesterday) {
|
|
|
8114
8317
|
async function getProjectStandup(name, project, dateStr, registry, config) {
|
|
8115
8318
|
const workspace = registry.forProject(name, config);
|
|
8116
8319
|
const spokeVault = resolveHome(project.spokeVault);
|
|
8117
|
-
const statusFile =
|
|
8320
|
+
const statusFile = path25.join(spokeVault, "status.md");
|
|
8118
8321
|
let status = {};
|
|
8119
|
-
if (
|
|
8322
|
+
if (fs21.existsSync(statusFile)) {
|
|
8120
8323
|
try {
|
|
8121
|
-
status = matter3(
|
|
8324
|
+
status = matter3(fs21.readFileSync(statusFile, "utf-8")).data;
|
|
8122
8325
|
} catch {
|
|
8123
8326
|
}
|
|
8124
8327
|
}
|
|
@@ -8236,15 +8439,15 @@ init_dist();
|
|
|
8236
8439
|
init_dist();
|
|
8237
8440
|
init_dist3();
|
|
8238
8441
|
import { Command as Command16 } from "commander";
|
|
8239
|
-
import
|
|
8240
|
-
import
|
|
8442
|
+
import fs22 from "fs";
|
|
8443
|
+
import path26 from "path";
|
|
8241
8444
|
import chalk17 from "chalk";
|
|
8242
8445
|
import matter4 from "gray-matter";
|
|
8243
8446
|
function readStatus(spokeVault) {
|
|
8244
|
-
const statusFile =
|
|
8245
|
-
if (!
|
|
8447
|
+
const statusFile = path26.join(spokeVault, "status.md");
|
|
8448
|
+
if (!fs22.existsSync(statusFile)) return {};
|
|
8246
8449
|
try {
|
|
8247
|
-
return matter4(
|
|
8450
|
+
return matter4(fs22.readFileSync(statusFile, "utf-8")).data;
|
|
8248
8451
|
} catch {
|
|
8249
8452
|
return {};
|
|
8250
8453
|
}
|
|
@@ -8554,9 +8757,9 @@ workspaceCommand.command("read").description("Print the contents of a scope-rela
|
|
|
8554
8757
|
const config = loadConfig();
|
|
8555
8758
|
const registry = buildRegistry2();
|
|
8556
8759
|
try {
|
|
8557
|
-
const { projectTarget, path:
|
|
8760
|
+
const { projectTarget, path: path29 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
|
|
8558
8761
|
const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
|
|
8559
|
-
const content = await driver.read(
|
|
8762
|
+
const content = await driver.read(path29);
|
|
8560
8763
|
process.stdout.write(content);
|
|
8561
8764
|
} catch (err) {
|
|
8562
8765
|
console.error(chalk19.red(err.message));
|
|
@@ -8568,26 +8771,26 @@ workspaceCommand.command("write").description("Write content to a scope-relative
|
|
|
8568
8771
|
const registry = buildRegistry2();
|
|
8569
8772
|
try {
|
|
8570
8773
|
let projectTarget;
|
|
8571
|
-
let
|
|
8774
|
+
let path29;
|
|
8572
8775
|
let rawContent;
|
|
8573
8776
|
if (opts.hub) {
|
|
8574
8777
|
if (arg3 !== void 0) {
|
|
8575
8778
|
throw new Error("With --hub, pass only the path and content");
|
|
8576
8779
|
}
|
|
8577
8780
|
projectTarget = void 0;
|
|
8578
|
-
|
|
8781
|
+
path29 = arg1;
|
|
8579
8782
|
rawContent = arg2;
|
|
8580
8783
|
} else {
|
|
8581
8784
|
if (arg3 === void 0) {
|
|
8582
8785
|
throw new Error("Missing content \u2014 usage: <project> <path> <content>");
|
|
8583
8786
|
}
|
|
8584
8787
|
projectTarget = arg1;
|
|
8585
|
-
|
|
8788
|
+
path29 = arg2;
|
|
8586
8789
|
rawContent = arg3;
|
|
8587
8790
|
}
|
|
8588
8791
|
const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
|
|
8589
8792
|
const payload = rawContent === "-" ? await readStdin() : rawContent;
|
|
8590
|
-
await driver.write(
|
|
8793
|
+
await driver.write(path29, payload);
|
|
8591
8794
|
} catch (err) {
|
|
8592
8795
|
console.error(chalk19.red(err.message));
|
|
8593
8796
|
process.exit(1);
|
|
@@ -8597,9 +8800,9 @@ workspaceCommand.command("list").description("List entries in a scope-relative d
|
|
|
8597
8800
|
const config = loadConfig();
|
|
8598
8801
|
const registry = buildRegistry2();
|
|
8599
8802
|
try {
|
|
8600
|
-
const { projectTarget, path:
|
|
8803
|
+
const { projectTarget, path: path29 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
|
|
8601
8804
|
const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
|
|
8602
|
-
const entries = await driver.list(
|
|
8805
|
+
const entries = await driver.list(path29);
|
|
8603
8806
|
for (const entry of entries) console.log(entry);
|
|
8604
8807
|
} catch (err) {
|
|
8605
8808
|
console.error(chalk19.red(err.message));
|
|
@@ -8610,9 +8813,9 @@ workspaceCommand.command("exists").description("Exit 0 if path exists, 1 if not"
|
|
|
8610
8813
|
const config = loadConfig();
|
|
8611
8814
|
const registry = buildRegistry2();
|
|
8612
8815
|
try {
|
|
8613
|
-
const { projectTarget, path:
|
|
8816
|
+
const { projectTarget, path: path29 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
|
|
8614
8817
|
const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
|
|
8615
|
-
const ok = await driver.exists(
|
|
8818
|
+
const ok = await driver.exists(path29);
|
|
8616
8819
|
process.exit(ok ? 0 : 1);
|
|
8617
8820
|
} catch (err) {
|
|
8618
8821
|
console.error(chalk19.red(err.message));
|
|
@@ -8623,9 +8826,9 @@ workspaceCommand.command("mkdir").description("Recursively create a scope-relati
|
|
|
8623
8826
|
const config = loadConfig();
|
|
8624
8827
|
const registry = buildRegistry2();
|
|
8625
8828
|
try {
|
|
8626
|
-
const { projectTarget, path:
|
|
8829
|
+
const { projectTarget, path: path29 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
|
|
8627
8830
|
const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
|
|
8628
|
-
await driver.mkdir(
|
|
8831
|
+
await driver.mkdir(path29);
|
|
8629
8832
|
} catch (err) {
|
|
8630
8833
|
console.error(chalk19.red(err.message));
|
|
8631
8834
|
process.exit(1);
|
|
@@ -8664,8 +8867,8 @@ init_dist3();
|
|
|
8664
8867
|
init_dist();
|
|
8665
8868
|
import { Command as Command20 } from "commander";
|
|
8666
8869
|
import chalk21 from "chalk";
|
|
8667
|
-
import
|
|
8668
|
-
import
|
|
8870
|
+
import fs23 from "fs";
|
|
8871
|
+
import path27 from "path";
|
|
8669
8872
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
8670
8873
|
function parseScope(v) {
|
|
8671
8874
|
if (v !== "user" && v !== "project") {
|
|
@@ -8674,10 +8877,10 @@ function parseScope(v) {
|
|
|
8674
8877
|
return v;
|
|
8675
8878
|
}
|
|
8676
8879
|
function findPackageRoot3() {
|
|
8677
|
-
let dir =
|
|
8880
|
+
let dir = path27.dirname(fileURLToPath4(import.meta.url));
|
|
8678
8881
|
while (dir !== "/" && dir !== "") {
|
|
8679
|
-
if (
|
|
8680
|
-
dir =
|
|
8882
|
+
if (fs23.existsSync(path27.join(dir, "package.json"))) return dir;
|
|
8883
|
+
dir = path27.dirname(dir);
|
|
8681
8884
|
}
|
|
8682
8885
|
return process.cwd();
|
|
8683
8886
|
}
|
|
@@ -8871,12 +9074,12 @@ init_dist();
|
|
|
8871
9074
|
init_dist();
|
|
8872
9075
|
init_dist();
|
|
8873
9076
|
import { Command as Command22 } from "commander";
|
|
8874
|
-
import
|
|
9077
|
+
import fs24 from "fs";
|
|
8875
9078
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
8876
|
-
import { dirname as dirname5, join as
|
|
9079
|
+
import { dirname as dirname5, join as join20 } from "path";
|
|
8877
9080
|
import chalk22 from "chalk";
|
|
8878
9081
|
function runConfigCheck(opts) {
|
|
8879
|
-
const raw = JSON.parse(
|
|
9082
|
+
const raw = JSON.parse(fs24.readFileSync(opts.configPath, "utf-8"));
|
|
8880
9083
|
const def = getDefaultConfig();
|
|
8881
9084
|
const items = detectDrift(raw, def);
|
|
8882
9085
|
let working = raw;
|
|
@@ -8893,7 +9096,7 @@ function runConfigCheck(opts) {
|
|
|
8893
9096
|
stamped = true;
|
|
8894
9097
|
}
|
|
8895
9098
|
if (opts.fix || opts.accept || stamped) {
|
|
8896
|
-
|
|
9099
|
+
fs24.writeFileSync(opts.configPath, JSON.stringify(working, null, 2) + "\n");
|
|
8897
9100
|
}
|
|
8898
9101
|
return { items, applied, remaining, stamped };
|
|
8899
9102
|
}
|
|
@@ -8927,6 +9130,22 @@ function runConfigSet(key, value, configPath = DEFAULT_CONFIG_PATH) {
|
|
|
8927
9130
|
node[parts[parts.length - 1]] = parsed;
|
|
8928
9131
|
saveConfig(config, configPath);
|
|
8929
9132
|
}
|
|
9133
|
+
function printRestartOutcome(outcome) {
|
|
9134
|
+
if (outcome === "skipped-not-running") {
|
|
9135
|
+
console.log(chalk22.dim("(daemon not running \u2014 change applies on next start)"));
|
|
9136
|
+
} else if (outcome === "skipped-opt-out") {
|
|
9137
|
+
console.log(chalk22.dim("(run 'squadrant heal daemon' to apply)"));
|
|
9138
|
+
}
|
|
9139
|
+
}
|
|
9140
|
+
function runConfigSetAction(opts) {
|
|
9141
|
+
runConfigSet(opts.key, opts.value, opts.configPath);
|
|
9142
|
+
console.log(chalk22.green(`\u2714 set ${opts.key} = ${opts.value}`));
|
|
9143
|
+
if (isDaemonCachedKey(opts.key)) {
|
|
9144
|
+
const doRestart = opts.doRestart ?? restartDaemonIfRunning;
|
|
9145
|
+
const outcome = doRestart({ reason: `config ${opts.key}`, noRestart: opts.noRestart });
|
|
9146
|
+
printRestartOutcome(outcome);
|
|
9147
|
+
}
|
|
9148
|
+
}
|
|
8930
9149
|
var SEV_COLOR = {
|
|
8931
9150
|
info: chalk22.green,
|
|
8932
9151
|
advisory: chalk22.yellow,
|
|
@@ -8948,7 +9167,7 @@ function printItems(items) {
|
|
|
8948
9167
|
var configCommand = new Command22("config").description("Inspect and reconcile squadrant config");
|
|
8949
9168
|
configCommand.command("check").description("Detect config drift vs the current default schema").option("--fix", "Apply the safe tier (add missing, remove deprecated)", false).option("--accept", "Stamp the current version without changing config (dismiss advisories)", false).option("--json", "Output drift items as JSON", false).action((opts) => {
|
|
8950
9169
|
const pkgVersion = readPkgVersion2();
|
|
8951
|
-
if (!
|
|
9170
|
+
if (!fs24.existsSync(DEFAULT_CONFIG_PATH)) {
|
|
8952
9171
|
console.log(chalk22.yellow("No config found \u2014 run `squadrant init` first."));
|
|
8953
9172
|
return;
|
|
8954
9173
|
}
|
|
@@ -8984,25 +9203,23 @@ configCommand.command("get").description("Read a config value by dotted key (e.g
|
|
|
8984
9203
|
process.exit(1);
|
|
8985
9204
|
}
|
|
8986
9205
|
});
|
|
8987
|
-
configCommand.command("set").description("Write a config value by dotted key (e.g. defaults.effort low)").argument("<key>", "dotted config key").argument("<value>", "value (JSON-parsed when possible, else a bare string)").action((key, value) => {
|
|
9206
|
+
configCommand.command("set").description("Write a config value by dotted key (e.g. defaults.effort low)").argument("<key>", "dotted config key").argument("<value>", "value (JSON-parsed when possible, else a bare string)").option("--no-restart", "skip daemon restart even if the key is daemon-cached").action((key, value, opts) => {
|
|
8988
9207
|
try {
|
|
8989
|
-
|
|
8990
|
-
console.log(chalk22.green(`\u2714 set ${key} = ${value}`));
|
|
9208
|
+
runConfigSetAction({ key, value, noRestart: opts.restart === false });
|
|
8991
9209
|
} catch (e) {
|
|
8992
9210
|
console.error(chalk22.red(e.message));
|
|
8993
9211
|
process.exit(1);
|
|
8994
9212
|
}
|
|
8995
9213
|
});
|
|
8996
9214
|
function readPkgVersion2() {
|
|
8997
|
-
const pkgPath =
|
|
8998
|
-
return JSON.parse(
|
|
9215
|
+
const pkgPath = join20(dirname5(fileURLToPath5(import.meta.url)), "..", "package.json");
|
|
9216
|
+
return JSON.parse(fs24.readFileSync(pkgPath, "utf-8")).version;
|
|
8999
9217
|
}
|
|
9000
9218
|
|
|
9001
9219
|
// packages/cli/src/commands/heal.ts
|
|
9002
9220
|
import { Command as Command23 } from "commander";
|
|
9003
9221
|
import chalk23 from "chalk";
|
|
9004
9222
|
init_dist2();
|
|
9005
|
-
init_dist2();
|
|
9006
9223
|
function buildHealStatus(components) {
|
|
9007
9224
|
if (components === null) {
|
|
9008
9225
|
return { healthy: false, daemonUnreachable: true, components: [] };
|
|
@@ -9082,7 +9299,7 @@ var healCommand = new Command23("heal").description("Targeted, idempotent remedi
|
|
|
9082
9299
|
).addCommand(
|
|
9083
9300
|
new Command23("daemon").description("Restart squadrantd via the idempotent launchd kickstart path").action(async () => {
|
|
9084
9301
|
const code = await runHealDaemon({
|
|
9085
|
-
ensureDaemon,
|
|
9302
|
+
ensureDaemon: () => restartDaemonIfRunning({ reason: "heal", isRunning: () => true }),
|
|
9086
9303
|
stdout: process.stdout,
|
|
9087
9304
|
stderr: process.stderr
|
|
9088
9305
|
});
|
|
@@ -9096,10 +9313,10 @@ init_dist2();
|
|
|
9096
9313
|
import { Command as Command24 } from "commander";
|
|
9097
9314
|
import { execSync as execSync13 } from "child_process";
|
|
9098
9315
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
9099
|
-
import { homedir as
|
|
9100
|
-
import { join as
|
|
9316
|
+
import { homedir as homedir14 } from "os";
|
|
9317
|
+
import { join as join21 } from "path";
|
|
9101
9318
|
import chalk24 from "chalk";
|
|
9102
|
-
var SOCK4 =
|
|
9319
|
+
var SOCK4 = join21(homedir14(), ".config", "squadrant", "squadrant.sock");
|
|
9103
9320
|
var WARMUP_TIMEOUT_MS = 12e4;
|
|
9104
9321
|
var WARMUP_POLL_MS = 1e3;
|
|
9105
9322
|
function resolveCurrentProject(config) {
|
|
@@ -9261,8 +9478,8 @@ var cmuxCommand = new Command25("cmux").description("cmux integration helpers").
|
|
|
9261
9478
|
|
|
9262
9479
|
// packages/cli/src/commands/effort.ts
|
|
9263
9480
|
init_dist();
|
|
9264
|
-
import
|
|
9265
|
-
import
|
|
9481
|
+
import fs25 from "fs";
|
|
9482
|
+
import path28 from "path";
|
|
9266
9483
|
import { Command as Command26 } from "commander";
|
|
9267
9484
|
import chalk26 from "chalk";
|
|
9268
9485
|
var VALID_EFFORTS = ["max", "balance", "low"];
|
|
@@ -9289,9 +9506,9 @@ function runEffortSet(value, configPath = DEFAULT_CONFIG_PATH) {
|
|
|
9289
9506
|
}
|
|
9290
9507
|
function canonical(p) {
|
|
9291
9508
|
try {
|
|
9292
|
-
return
|
|
9509
|
+
return fs25.realpathSync(p);
|
|
9293
9510
|
} catch {
|
|
9294
|
-
return
|
|
9511
|
+
return path28.resolve(p);
|
|
9295
9512
|
}
|
|
9296
9513
|
}
|
|
9297
9514
|
async function notifyCaptainsOfEffort(effort, config, driver, cwd = process.cwd()) {
|
|
@@ -9338,12 +9555,12 @@ var effortCommand = new Command26("effort").description("Get or set the global c
|
|
|
9338
9555
|
// packages/cli/src/commands/telegram.ts
|
|
9339
9556
|
init_dist();
|
|
9340
9557
|
init_dist2();
|
|
9341
|
-
import { join as
|
|
9558
|
+
import { join as join22, dirname as dirname6 } from "path";
|
|
9342
9559
|
import { emitKeypressEvents } from "readline";
|
|
9343
9560
|
import { Command as Command27 } from "commander";
|
|
9344
9561
|
import chalk27 from "chalk";
|
|
9345
9562
|
function defaultStateRoot() {
|
|
9346
|
-
return
|
|
9563
|
+
return join22(dirname6(DEFAULT_CONFIG_PATH), "state");
|
|
9347
9564
|
}
|
|
9348
9565
|
function runTelegramStatus(opts) {
|
|
9349
9566
|
const tg = opts.config.telegram;
|
|
@@ -9363,6 +9580,34 @@ async function runTelegramSend(opts) {
|
|
|
9363
9580
|
await opts.client.sendMessage(opts.cfg.supergroupId, topicId, opts.message);
|
|
9364
9581
|
return { chatId: opts.cfg.supergroupId, topicId };
|
|
9365
9582
|
}
|
|
9583
|
+
function runTelegramNotifySet(opts) {
|
|
9584
|
+
setNotify(opts.stateRoot, opts.project, opts.active);
|
|
9585
|
+
}
|
|
9586
|
+
function runTelegramNotifyPref(args) {
|
|
9587
|
+
const { project, dimension, value, root } = args;
|
|
9588
|
+
if (dimension === "crew") {
|
|
9589
|
+
if (!["all", "alert_only", "done_only", "none"].includes(value))
|
|
9590
|
+
return { ok: false, message: "crew must be all|alert_only|done_only|none" };
|
|
9591
|
+
saveProjectOverride(project, { telegram: { notify: { crew: value } } }, root);
|
|
9592
|
+
return { ok: true };
|
|
9593
|
+
}
|
|
9594
|
+
if (value !== "on" && value !== "off") return { ok: false, message: "cap must be on|off" };
|
|
9595
|
+
saveProjectOverride(project, { telegram: { notify: { cap: value === "on" } } }, root);
|
|
9596
|
+
return { ok: true };
|
|
9597
|
+
}
|
|
9598
|
+
function capAllowed(project, globalNotify, root) {
|
|
9599
|
+
return resolveNotify(globalNotify, loadProjectOverride(project, root)).cap;
|
|
9600
|
+
}
|
|
9601
|
+
function runTelegramNotifyStatus(opts) {
|
|
9602
|
+
const s = loadState(opts.stateRoot);
|
|
9603
|
+
const projects = /* @__PURE__ */ new Set();
|
|
9604
|
+
for (const key of Object.keys(s.topics)) {
|
|
9605
|
+
const sep2 = key.indexOf("::");
|
|
9606
|
+
projects.add(sep2 === -1 ? key : key.slice(0, sep2));
|
|
9607
|
+
}
|
|
9608
|
+
for (const p of Object.keys(s.notify)) projects.add(p);
|
|
9609
|
+
return [...projects].map((project) => ({ project, active: s.notify[project] === true }));
|
|
9610
|
+
}
|
|
9366
9611
|
async function runTelegramLink(opts) {
|
|
9367
9612
|
const existing = loadState(opts.stateRoot).topics[topicKey(opts.project)];
|
|
9368
9613
|
if (existing !== void 0) return { topicId: existing, created: false };
|
|
@@ -9413,6 +9658,44 @@ async function questionYesNo(prompt) {
|
|
|
9413
9658
|
});
|
|
9414
9659
|
});
|
|
9415
9660
|
}
|
|
9661
|
+
function confirmationText(project, before, after, dim) {
|
|
9662
|
+
if (dim === "active") return `\u{1F515} ${project} \u2014 all notifications muted here. Unmute: squadrant telegram notify ${project} on`;
|
|
9663
|
+
if (dim === "cap") return `\u{1F515} ${project} \u2014 captain messages muted here. Re-enable: squadrant telegram notify ${project} cap on`;
|
|
9664
|
+
return `\u{1F515} ${project} \u2014 crew notifications now '${after.crew}' (was '${before.crew}'). Re-enable: squadrant telegram notify ${project} crew ${before.crew}`;
|
|
9665
|
+
}
|
|
9666
|
+
async function runNotifyConfirmation(opts) {
|
|
9667
|
+
const { quieter, dim } = isQuieter(opts.before, opts.after);
|
|
9668
|
+
if (!quieter || dim === null) return false;
|
|
9669
|
+
const topicId = loadState(opts.stateRoot).topics[topicKey(opts.project)];
|
|
9670
|
+
if (topicId === void 0) return false;
|
|
9671
|
+
const text = confirmationText(opts.project, opts.before, opts.after, dim);
|
|
9672
|
+
try {
|
|
9673
|
+
await opts.client.sendMessage(opts.cfg.supergroupId, topicId, text);
|
|
9674
|
+
return true;
|
|
9675
|
+
} catch {
|
|
9676
|
+
console.warn(`[squadrant] mute-confirmation send failed for ${opts.project} \u2014 notification preference was still saved`);
|
|
9677
|
+
return false;
|
|
9678
|
+
}
|
|
9679
|
+
}
|
|
9680
|
+
function resolveSetupToken(existingToken, opts) {
|
|
9681
|
+
if (opts.resetToken || !existingToken) return "prompt";
|
|
9682
|
+
return "try-reuse";
|
|
9683
|
+
}
|
|
9684
|
+
function resolveSetupUserId(flagUserId, detectedUserId, stateRoot) {
|
|
9685
|
+
return flagUserId ?? detectedUserId ?? loadState(stateRoot).lastUserId;
|
|
9686
|
+
}
|
|
9687
|
+
async function runRegisterCommands(opts) {
|
|
9688
|
+
await opts.client.setMyCommands(BOT_COMMANDS);
|
|
9689
|
+
}
|
|
9690
|
+
function runTelegramPostSetup(opts) {
|
|
9691
|
+
const doRestart = opts.doRestart ?? restartDaemonIfRunning;
|
|
9692
|
+
const outcome = doRestart({ reason: "telegram config" });
|
|
9693
|
+
if (outcome === "skipped-not-running") {
|
|
9694
|
+
console.log(chalk27.dim("(daemon not running \u2014 change applies on next start)"));
|
|
9695
|
+
} else if (outcome === "skipped-opt-out") {
|
|
9696
|
+
console.log(chalk27.dim("(run 'squadrant heal daemon' to apply)"));
|
|
9697
|
+
}
|
|
9698
|
+
}
|
|
9416
9699
|
var telegramCommand = new Command27("telegram").description("Two-way Telegram integration: push crew events to a topic and reply into the captain pane");
|
|
9417
9700
|
telegramCommand.command("status").description("Show Telegram config and linked projects").action(() => {
|
|
9418
9701
|
const { tokenSet, supergroupId, links } = runTelegramStatus({ config: loadConfig(), stateRoot: defaultStateRoot() });
|
|
@@ -9439,7 +9722,7 @@ telegramCommand.command("link").argument("<project>", "project to bind to a Tele
|
|
|
9439
9722
|
const { topicId, created } = await runTelegramLink({ project, cfg, client, stateRoot: defaultStateRoot() });
|
|
9440
9723
|
console.log(chalk27.green(`${created ? "linked" : "already linked"}: ${project} \u2192 topic ${topicId}`));
|
|
9441
9724
|
});
|
|
9442
|
-
telegramCommand.command("setup").description("Interactive wizard \u2014 bot token, validate, auto-detect supergroup, write config").action(async () => {
|
|
9725
|
+
telegramCommand.command("setup").description("Interactive wizard \u2014 bot token, validate, auto-detect supergroup, write config").option("--reset-token", "force re-entry of the bot token even if one already exists").option("--redetect", "force group re-detection even when a supergroup is already configured").option("--user-id <id>", "allowlist user-id \u2014 enables remote control on a re-run without getUpdates detection", (v) => parseInt(v, 10)).action(async (opts) => {
|
|
9443
9726
|
if (!process.stdin.isTTY) {
|
|
9444
9727
|
console.error(chalk27.red("setup requires a TTY \u2014 pipe input is not supported"));
|
|
9445
9728
|
process.exit(1);
|
|
@@ -9453,63 +9736,200 @@ telegramCommand.command("setup").description("Interactive wizard \u2014 bot toke
|
|
|
9453
9736
|
console.log(" 3. Bot privacy mode set to OFF (@BotFather \u2192 /setprivacy \u2192 Disable)");
|
|
9454
9737
|
console.log();
|
|
9455
9738
|
console.log(chalk27.bold("Step 1/3 \u2014 Bot token"));
|
|
9456
|
-
|
|
9457
|
-
const
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
|
|
9461
|
-
}
|
|
9462
|
-
const client = createTelegramClient({ token });
|
|
9739
|
+
const existingCfg = loadConfig().telegram;
|
|
9740
|
+
const existingToken = existingCfg?.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
|
|
9741
|
+
const decision = resolveSetupToken(existingToken, { resetToken: opts.resetToken ?? false });
|
|
9742
|
+
let token;
|
|
9743
|
+
let client;
|
|
9463
9744
|
let botUser;
|
|
9464
|
-
try {
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
|
|
9468
|
-
|
|
9745
|
+
if (decision === "try-reuse") {
|
|
9746
|
+
client = createTelegramClient({ token: existingToken });
|
|
9747
|
+
try {
|
|
9748
|
+
botUser = await client.getMe();
|
|
9749
|
+
token = existingToken;
|
|
9750
|
+
console.log(chalk27.green(`Using existing bot token (@${botUser.username})`));
|
|
9751
|
+
console.log();
|
|
9752
|
+
} catch {
|
|
9753
|
+
console.log(chalk27.yellow("Existing token is invalid \u2014 please enter a new one."));
|
|
9754
|
+
console.log("Paste your bot token then press Enter (input is hidden):");
|
|
9755
|
+
token = await questionMasked();
|
|
9756
|
+
if (!token) {
|
|
9757
|
+
console.error(chalk27.red("token required"));
|
|
9758
|
+
process.exit(1);
|
|
9759
|
+
}
|
|
9760
|
+
client = createTelegramClient({ token });
|
|
9761
|
+
try {
|
|
9762
|
+
botUser = await client.getMe();
|
|
9763
|
+
} catch (e) {
|
|
9764
|
+
console.error(chalk27.red(`token rejected: ${e.message}`));
|
|
9765
|
+
process.exit(1);
|
|
9766
|
+
}
|
|
9767
|
+
console.log(chalk27.green(`Connected as @${botUser.username}`));
|
|
9768
|
+
console.log();
|
|
9769
|
+
}
|
|
9770
|
+
} else {
|
|
9771
|
+
console.log("Paste your bot token then press Enter (input is hidden):");
|
|
9772
|
+
token = await questionMasked();
|
|
9773
|
+
if (!token) {
|
|
9774
|
+
console.error(chalk27.red("token required"));
|
|
9775
|
+
process.exit(1);
|
|
9776
|
+
}
|
|
9777
|
+
client = createTelegramClient({ token });
|
|
9778
|
+
try {
|
|
9779
|
+
botUser = await client.getMe();
|
|
9780
|
+
} catch (e) {
|
|
9781
|
+
console.error(chalk27.red(`token rejected: ${e.message}`));
|
|
9782
|
+
process.exit(1);
|
|
9783
|
+
}
|
|
9784
|
+
console.log(chalk27.green(`Connected as @${botUser.username}`));
|
|
9785
|
+
console.log();
|
|
9469
9786
|
}
|
|
9470
|
-
console.log(chalk27.
|
|
9471
|
-
|
|
9472
|
-
console.log(chalk27.bold("Step 2/3 \u2014 Find your group"));
|
|
9473
|
-
console.log("Add the bot to your forum supergroup, then send any message in it.");
|
|
9474
|
-
console.log(chalk27.dim("Waiting for a message (up to 60s)\u2026"));
|
|
9787
|
+
console.log(chalk27.bold("Step 2/3 \u2014 Supergroup"));
|
|
9788
|
+
const groupDecision = resolveSetupGroup(existingCfg?.supergroupId, { redetect: opts.redetect ?? false });
|
|
9475
9789
|
let supergroupId;
|
|
9476
|
-
let
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
|
|
9480
|
-
console.
|
|
9481
|
-
|
|
9482
|
-
|
|
9790
|
+
let detectedUserId;
|
|
9791
|
+
if (groupDecision === "reuse") {
|
|
9792
|
+
supergroupId = existingCfg.supergroupId;
|
|
9793
|
+
console.log(chalk27.green(`Using existing group: ${supergroupId}`));
|
|
9794
|
+
console.log();
|
|
9795
|
+
} else {
|
|
9796
|
+
console.log("Add the bot to your forum supergroup, then send any message in it.");
|
|
9797
|
+
console.log(chalk27.dim("Waiting for a message (up to 60s)\u2026"));
|
|
9798
|
+
try {
|
|
9799
|
+
({ supergroupId, userId: detectedUserId } = await detectGroupAndUser(client, { timeoutMs: 6e4 }));
|
|
9800
|
+
} catch {
|
|
9801
|
+
console.error(chalk27.red("Timed out \u2014 no supergroup message received within 60s."));
|
|
9802
|
+
console.error(chalk27.yellow("Check: bot is an admin in the group \xB7 privacy mode is OFF \xB7 Topics enabled"));
|
|
9803
|
+
process.exit(1);
|
|
9804
|
+
}
|
|
9805
|
+
console.log(chalk27.green(`Found group: ${supergroupId}`));
|
|
9806
|
+
console.log();
|
|
9483
9807
|
}
|
|
9484
|
-
console.log(chalk27.green(`Found group: ${supergroupId}`));
|
|
9485
|
-
console.log();
|
|
9486
9808
|
console.log(chalk27.bold("Step 3/3 \u2014 Remote control + Save"));
|
|
9487
9809
|
console.log(chalk27.dim("Remote control enables auto-launching captains and the General command channel"));
|
|
9488
9810
|
console.log(chalk27.dim("from your phone \u2014 gated to your Telegram user-id only (fail-closed)."));
|
|
9811
|
+
const finalUserId = resolveSetupUserId(opts.userId, detectedUserId, defaultStateRoot());
|
|
9489
9812
|
let users;
|
|
9490
9813
|
let remoteControl;
|
|
9491
|
-
|
|
9492
|
-
|
|
9493
|
-
console.log(chalk27.yellow("Re-run setup once it's available, or edit telegram.users in config manually."));
|
|
9494
|
-
} else {
|
|
9814
|
+
let printedRemoteControlState = false;
|
|
9815
|
+
if (finalUserId !== void 0) {
|
|
9495
9816
|
const enable = await questionYesNo(
|
|
9496
|
-
`Enable remote control for your user-id ${
|
|
9817
|
+
`Enable remote control for your user-id ${finalUserId}? [y/N] `
|
|
9497
9818
|
);
|
|
9498
9819
|
if (enable) {
|
|
9499
|
-
users = [
|
|
9820
|
+
users = [finalUserId];
|
|
9500
9821
|
remoteControl = true;
|
|
9501
9822
|
}
|
|
9823
|
+
} else if (groupDecision === "detect") {
|
|
9824
|
+
console.log(chalk27.yellow("Could not read your user-id from that message \u2014 skipping remote control."));
|
|
9825
|
+
console.log(chalk27.yellow("Re-run with --user-id <id> to enable, or edit telegram.users in config manually."));
|
|
9826
|
+
printedRemoteControlState = true;
|
|
9827
|
+
} else {
|
|
9828
|
+
const existingUsers = existingCfg?.users;
|
|
9829
|
+
if (existingUsers && existingUsers.length > 0) {
|
|
9830
|
+
console.log(chalk27.dim(`Remote control: already configured (user-id ${existingUsers[0]}). Use --user-id to update.`));
|
|
9831
|
+
} else {
|
|
9832
|
+
console.log(chalk27.dim("Remote control: off. Re-run with --user-id <id> to enable."));
|
|
9833
|
+
}
|
|
9834
|
+
printedRemoteControlState = true;
|
|
9502
9835
|
}
|
|
9503
9836
|
writeTelegramConfig(DEFAULT_CONFIG_PATH, { token, supergroupId, users, remoteControl });
|
|
9504
9837
|
console.log(chalk27.green(`Wrote telegram config \u2014 token: ${maskToken(token)} group: ${supergroupId}`));
|
|
9505
|
-
if (
|
|
9506
|
-
|
|
9838
|
+
if (!printedRemoteControlState) {
|
|
9839
|
+
if (remoteControl) {
|
|
9840
|
+
console.log(chalk27.green(`Remote control: ON (allowlisted user-id ${users[0]})`));
|
|
9841
|
+
} else {
|
|
9842
|
+
console.log(chalk27.dim("Remote control: off (default). Re-run with --user-id <id> to enable."));
|
|
9843
|
+
}
|
|
9844
|
+
}
|
|
9845
|
+
try {
|
|
9846
|
+
await runRegisterCommands({ client });
|
|
9847
|
+
console.log(chalk27.dim("Registered the /command menu."));
|
|
9848
|
+
} catch (e) {
|
|
9849
|
+
console.log(chalk27.yellow(`command-menu registration skipped: ${e.message}`));
|
|
9850
|
+
}
|
|
9851
|
+
const topics = loadState(defaultStateRoot()).topics;
|
|
9852
|
+
const topicEntries = Object.entries(topics);
|
|
9853
|
+
if (topicEntries.length > 0) {
|
|
9854
|
+
const summary = topicEntries.map(([key, id]) => {
|
|
9855
|
+
const project = key.slice(0, key.indexOf("::"));
|
|
9856
|
+
return `${project}\u2192${id}`;
|
|
9857
|
+
}).join(", ");
|
|
9858
|
+
console.log(chalk27.dim(`Existing topics: ${summary} (already created \u2014 not recreated)`));
|
|
9507
9859
|
} else {
|
|
9508
|
-
console.log(chalk27.dim("
|
|
9860
|
+
console.log(chalk27.dim("No project topics yet \u2014 they're created on first delivery or via: squadrant telegram link <project>"));
|
|
9509
9861
|
}
|
|
9862
|
+
runTelegramPostSetup({});
|
|
9510
9863
|
console.log();
|
|
9511
9864
|
console.log(`Next: ${chalk27.cyan("squadrant telegram link <project>")}`);
|
|
9512
9865
|
});
|
|
9866
|
+
telegramCommand.command("register-commands").description("Register (or re-register) the bot's / command menu with Telegram").action(async () => {
|
|
9867
|
+
const cfg = loadConfig().telegram;
|
|
9868
|
+
if (!cfg) {
|
|
9869
|
+
console.error(chalk27.red("telegram config absent \u2014 run: squadrant telegram setup"));
|
|
9870
|
+
process.exit(1);
|
|
9871
|
+
}
|
|
9872
|
+
const token = cfg.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
|
|
9873
|
+
if (!token) {
|
|
9874
|
+
console.error(chalk27.red("no botToken in config and TELEGRAM_BOT_TOKEN is unset"));
|
|
9875
|
+
process.exit(1);
|
|
9876
|
+
}
|
|
9877
|
+
const client = createTelegramClient({ token });
|
|
9878
|
+
await runRegisterCommands({ client });
|
|
9879
|
+
console.log(chalk27.green(`registered ${BOT_COMMANDS.length} bot commands`));
|
|
9880
|
+
});
|
|
9881
|
+
telegramCommand.command("notify").argument("[project]", "project to toggle").argument("[state]", "on | off | crew | cap").argument("[value]", "tier for crew (all|alert_only|done_only|none) or on|off for cap").option("--status", "list notification state for all projects").description("Live on|off (state), or crew <tier> / cap <on|off> preference (per-project config)").action(async (project, state, value, opts) => {
|
|
9882
|
+
const stateRoot = defaultStateRoot();
|
|
9883
|
+
if (opts.status || !project) {
|
|
9884
|
+
const rows = runTelegramNotifyStatus({ stateRoot });
|
|
9885
|
+
if (rows.length === 0) {
|
|
9886
|
+
console.log("no projects linked");
|
|
9887
|
+
return;
|
|
9888
|
+
}
|
|
9889
|
+
for (const r of rows) {
|
|
9890
|
+
console.log(` ${r.project}: ${r.active ? chalk27.green("on") : chalk27.dim("off (muted)")}`);
|
|
9891
|
+
}
|
|
9892
|
+
return;
|
|
9893
|
+
}
|
|
9894
|
+
const tgCfg = loadConfig().telegram;
|
|
9895
|
+
const globalNotify = tgCfg?.notify;
|
|
9896
|
+
const token = tgCfg?.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
|
|
9897
|
+
if (state === "crew" || state === "cap") {
|
|
9898
|
+
if (value === void 0) {
|
|
9899
|
+
console.error(chalk27.red(`usage: squadrant telegram notify <project> ${state} <value>`));
|
|
9900
|
+
process.exit(1);
|
|
9901
|
+
}
|
|
9902
|
+
const resolved2 = resolveNotify(globalNotify, loadProjectOverride(project));
|
|
9903
|
+
const before2 = { ...resolved2, active: isNotifyActive(stateRoot, project) };
|
|
9904
|
+
const res = runTelegramNotifyPref({ project, dimension: state, value });
|
|
9905
|
+
if (!res.ok) {
|
|
9906
|
+
console.error(chalk27.red(res.message));
|
|
9907
|
+
process.exit(1);
|
|
9908
|
+
}
|
|
9909
|
+
console.log(chalk27.green(`${project} ${state} = ${value}`));
|
|
9910
|
+
const after2 = state === "crew" ? { ...before2, crew: value } : { ...before2, cap: value === "on" };
|
|
9911
|
+
if (tgCfg && token) {
|
|
9912
|
+
const client = createTelegramClient({ token });
|
|
9913
|
+
const sent = await runNotifyConfirmation({ project, before: before2, after: after2, cfg: tgCfg, client, stateRoot });
|
|
9914
|
+
if (sent) console.log(chalk27.dim(`\u2192 notified ${project} topic`));
|
|
9915
|
+
}
|
|
9916
|
+
return;
|
|
9917
|
+
}
|
|
9918
|
+
if (state !== "on" && state !== "off") {
|
|
9919
|
+
console.error(chalk27.red("usage: squadrant telegram notify <project> <on|off|crew <tier>|cap <on|off>>"));
|
|
9920
|
+
process.exit(1);
|
|
9921
|
+
}
|
|
9922
|
+
const resolved = resolveNotify(globalNotify, loadProjectOverride(project));
|
|
9923
|
+
const before = { ...resolved, active: isNotifyActive(stateRoot, project) };
|
|
9924
|
+
const after = { ...before, active: state === "on" };
|
|
9925
|
+
runTelegramNotifySet({ project, active: state === "on", stateRoot });
|
|
9926
|
+
console.log(chalk27.green(`${project} notifications ${state === "on" ? "ON" : "OFF"}`));
|
|
9927
|
+
if (tgCfg && token) {
|
|
9928
|
+
const client = createTelegramClient({ token });
|
|
9929
|
+
const sent = await runNotifyConfirmation({ project, before, after, cfg: tgCfg, client, stateRoot });
|
|
9930
|
+
if (sent) console.log(chalk27.dim(`\u2192 notified ${project} topic`));
|
|
9931
|
+
}
|
|
9932
|
+
});
|
|
9513
9933
|
telegramCommand.command("send").argument("<project>", "project whose topic receives the message").argument("[message...]", "message text (omit to read from stdin)").description("Send a message to a project's linked Telegram topic").action(async (project, messageParts) => {
|
|
9514
9934
|
const cfg = loadConfig().telegram;
|
|
9515
9935
|
if (!cfg) {
|
|
@@ -9521,6 +9941,10 @@ telegramCommand.command("send").argument("<project>", "project whose topic recei
|
|
|
9521
9941
|
console.error(chalk27.red("no botToken in config and TELEGRAM_BOT_TOKEN is unset"));
|
|
9522
9942
|
process.exit(1);
|
|
9523
9943
|
}
|
|
9944
|
+
if (!capAllowed(project, cfg.notify)) {
|
|
9945
|
+
console.log(chalk27.dim(`${project}: captain messages muted (cap=off) \u2014 not sent`));
|
|
9946
|
+
return;
|
|
9947
|
+
}
|
|
9524
9948
|
let message;
|
|
9525
9949
|
if (messageParts.length > 0) {
|
|
9526
9950
|
message = messageParts.join(" ");
|
|
@@ -9553,15 +9977,15 @@ init_dist();
|
|
|
9553
9977
|
init_dist();
|
|
9554
9978
|
init_dist();
|
|
9555
9979
|
var __dirname = dirname7(fileURLToPath6(import.meta.url));
|
|
9556
|
-
var pkg = JSON.parse(readFileSync11(
|
|
9980
|
+
var pkg = JSON.parse(readFileSync11(join23(__dirname, "..", "package.json"), "utf-8"));
|
|
9557
9981
|
ensureRuntimeSynced({
|
|
9558
|
-
sourceRoot:
|
|
9559
|
-
runtimeRoot:
|
|
9982
|
+
sourceRoot: join23(__dirname, ".."),
|
|
9983
|
+
runtimeRoot: join23(homedir15(), ".config", "squadrant")
|
|
9560
9984
|
});
|
|
9561
9985
|
if (process.argv[2] !== "config") {
|
|
9562
9986
|
try {
|
|
9563
|
-
const cfgPath =
|
|
9564
|
-
if (
|
|
9987
|
+
const cfgPath = join23(homedir15(), ".config", "squadrant", "config.json");
|
|
9988
|
+
if (existsSync11(cfgPath)) {
|
|
9565
9989
|
const cfg = JSON.parse(readFileSync11(cfgPath, "utf-8"));
|
|
9566
9990
|
if (needsCheck(cfg, pkg.version)) {
|
|
9567
9991
|
const items = detectDrift(cfg, getDefaultConfig());
|