bosun 0.33.1 → 0.33.2

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/config.mjs CHANGED
@@ -65,7 +65,8 @@ function isWslInteropRuntime() {
65
65
  }
66
66
 
67
67
  function resolveConfigDir(repoRoot) {
68
- // 1. Explicit env override
68
+ // 1. Explicit env override (BOSUN_HOME supersedes BOSUN_DIR; both are aliases)
69
+ if (process.env.BOSUN_HOME) return resolve(process.env.BOSUN_HOME);
69
70
  if (process.env.BOSUN_DIR) return resolve(process.env.BOSUN_DIR);
70
71
 
71
72
  // 2. Platform-aware user home
@@ -86,6 +87,55 @@ function resolveConfigDir(repoRoot) {
86
87
  return resolve(baseDir, "bosun");
87
88
  }
88
89
 
90
+ function getConfigSearchDirs(repoRoot) {
91
+ const dirs = new Set();
92
+ if (process.env.BOSUN_HOME) dirs.add(resolve(process.env.BOSUN_HOME));
93
+ if (process.env.BOSUN_DIR) dirs.add(resolve(process.env.BOSUN_DIR));
94
+ dirs.add(resolveConfigDir(repoRoot));
95
+ if (process.env.APPDATA) dirs.add(resolve(process.env.APPDATA, "bosun"));
96
+ if (process.env.LOCALAPPDATA) dirs.add(resolve(process.env.LOCALAPPDATA, "bosun"));
97
+ if (process.env.USERPROFILE) dirs.add(resolve(process.env.USERPROFILE, "bosun"));
98
+ if (process.env.HOME) dirs.add(resolve(process.env.HOME, "bosun"));
99
+ return [...dirs].filter(Boolean);
100
+ }
101
+
102
+ function collectRepoPathsFromConfig(cfg, configDir) {
103
+ const paths = [];
104
+ const pushPath = (path) => {
105
+ if (!path) return;
106
+ paths.push(isAbsolute(path) ? path : resolve(configDir, path));
107
+ };
108
+
109
+ const repos = cfg.repositories || cfg.repos || [];
110
+ if (Array.isArray(repos)) {
111
+ for (const repo of repos) {
112
+ const repoPath = typeof repo === "string" ? repo : (repo?.path || repo?.repoRoot);
113
+ pushPath(repoPath);
114
+ }
115
+ }
116
+
117
+ const workspaces = cfg.workspaces || [];
118
+ if (Array.isArray(workspaces)) {
119
+ for (const ws of workspaces) {
120
+ const wsBase = ws?.path
121
+ ? (isAbsolute(ws.path) ? ws.path : resolve(configDir, ws.path))
122
+ : (ws?.id ? resolve(configDir, "workspaces", ws.id) : null);
123
+ const wsRepos = ws?.repos || ws?.repositories || [];
124
+ if (!Array.isArray(wsRepos)) continue;
125
+ for (const repo of wsRepos) {
126
+ const repoPath = typeof repo === "string" ? repo : (repo?.path || repo?.repoRoot);
127
+ if (repoPath) {
128
+ pushPath(repoPath);
129
+ continue;
130
+ }
131
+ if (wsBase && repo?.name) pushPath(resolve(wsBase, repo.name));
132
+ }
133
+ }
134
+ }
135
+
136
+ return paths;
137
+ }
138
+
89
139
  function ensurePromptWorkspaceGitIgnore(repoRoot) {
90
140
  const gitignorePath = resolve(repoRoot, ".gitignore");
91
141
  const entry = "/.bosun/";
@@ -575,33 +625,26 @@ function detectRepoRoot() {
575
625
  }
576
626
 
577
627
  // 4. Check bosun config for workspace repos
578
- const configDir = process.env.BOSUN_DIR || resolve(process.env.HOME || process.env.USERPROFILE || "", "bosun");
628
+ const configDirs = getConfigSearchDirs();
629
+ let fallbackRepo = null;
579
630
  for (const cfgName of CONFIG_FILES) {
580
- const cfgPath = resolve(configDir, cfgName);
581
- if (existsSync(cfgPath)) {
631
+ for (const configDir of configDirs) {
632
+ const cfgPath = resolve(configDir, cfgName);
633
+ if (!existsSync(cfgPath)) continue;
582
634
  try {
583
635
  const cfg = JSON.parse(readFileSync(cfgPath, "utf8"));
584
- // Check workspace repos
585
- const repos = cfg.repositories || cfg.repos || [];
586
- if (Array.isArray(repos) && repos.length > 0) {
587
- const primary = repos.find((r) => r.primary) || repos[0];
588
- const repoPath = primary?.path || primary?.repoRoot;
589
- if (repoPath && existsSync(resolve(repoPath))) return resolve(repoPath);
636
+ const repoPaths = collectRepoPathsFromConfig(cfg, configDir);
637
+ for (const repoPath of repoPaths) {
638
+ if (!repoPath || !existsSync(repoPath)) continue;
639
+ if (existsSync(resolve(repoPath, ".git"))) return repoPath;
640
+ fallbackRepo ??= repoPath;
590
641
  }
591
- // Check workspaces
592
- const workspaces = cfg.workspaces;
593
- if (Array.isArray(workspaces) && workspaces.length > 0) {
594
- const ws = workspaces[0];
595
- const wsRepos = ws?.repos || ws?.repositories || [];
596
- if (Array.isArray(wsRepos) && wsRepos.length > 0) {
597
- const primary = wsRepos.find((r) => r.primary) || wsRepos[0];
598
- const repoPath = primary?.path || primary?.repoRoot;
599
- if (repoPath && existsSync(resolve(repoPath))) return resolve(repoPath);
600
- }
601
- }
602
- } catch { /* invalid config */ }
642
+ } catch {
643
+ /* invalid config */
644
+ }
603
645
  }
604
646
  }
647
+ if (fallbackRepo) return fallbackRepo;
605
648
 
606
649
  // 5. Final fallback — warn and return cwd. This is unlikely to be a valid
607
650
  // git repo (e.g. when the daemon spawns with cwd=homedir), but returning
@@ -2069,6 +2112,25 @@ export function loadConfig(argv = process.argv, options = {}) {
2069
2112
 
2070
2113
  // First run
2071
2114
  isFirstRun,
2115
+
2116
+ // Security controls
2117
+ security: Object.freeze({
2118
+ // List of trusted issue creators (primary GitHub account or configured list)
2119
+ trustedCreators:
2120
+ configData.trustedCreators ||
2121
+ process.env.BOSUN_TRUSTED_CREATORS?.split(",") ||
2122
+ [],
2123
+ // Enforce all new tasks go to backlog unless planner config allows auto-push
2124
+ enforceBacklog:
2125
+ typeof configData.enforceBacklog === "boolean"
2126
+ ? configData.enforceBacklog
2127
+ : true,
2128
+ // Control agent triggers: restrict agent activation to trusted sources
2129
+ agentTriggerControl:
2130
+ typeof configData.agentTriggerControl === "boolean"
2131
+ ? configData.agentTriggerControl
2132
+ : true,
2133
+ }),
2072
2134
  };
2073
2135
 
2074
2136
  return Object.freeze(config);