@the-open-engine/zeroshot 6.1.0 → 6.3.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 +141 -474
- package/cli/commands/cmdproof.js +180 -6
- package/cli/event-copy.js +12 -0
- package/cli/index.js +311 -96
- package/cli/message-formatters-normal.js +14 -2
- package/cli/message-formatters-watch.js +9 -2
- package/cluster-hooks/block-ask-user-question.py +53 -0
- package/cluster-hooks/block-dangerous-git.py +145 -0
- package/lib/clusters-registry.js +48 -0
- package/lib/compose-utils.js +101 -0
- package/lib/detached-startup.js +9 -0
- package/lib/id-detector.js +8 -9
- package/lib/path-check.js +63 -0
- package/lib/run-mode.js +22 -0
- package/lib/start-cluster.js +39 -23
- package/package.json +3 -4
- package/scripts/check-path.js +19 -0
- package/scripts/validate-templates.js +11 -29
- package/src/agent/agent-hook-executor.js +1 -1
- package/src/agent/agent-task-executor.js +4 -3
- package/src/agent/pr-verification.js +27 -1
- package/src/agents/git-pusher-template.js +121 -4
- package/src/attach/socket-discovery.js +2 -3
- package/src/claude-credentials.js +75 -0
- package/src/claude-task-runner.js +1 -0
- package/src/isolation-manager.js +12 -9
- package/src/issue-providers/README.md +45 -15
- package/src/issue-providers/index.js +2 -0
- package/src/issue-providers/jira-provider.js +8 -1
- package/src/issue-providers/linear-provider.js +405 -0
- package/src/ledger.js +42 -3
- package/src/lib/gc.js +2 -3
- package/src/message-bus.js +10 -0
- package/src/orchestrator.js +264 -144
- package/src/preflight.js +12 -16
- package/src/providers/base-provider.js +7 -6
- package/src/status-footer.js +13 -1
- package/src/template-validation/index.js +3 -0
- package/src/template-validation/report-formatter.js +55 -0
- package/src/worktree-claude-config.js +2 -13
- package/task-lib/runner.js +1 -0
- package/task-lib/watcher.js +1 -0
package/src/preflight.js
CHANGED
|
@@ -21,6 +21,7 @@ const {
|
|
|
21
21
|
const { loadSettings, getClaudeCommand } = require('../lib/settings.js');
|
|
22
22
|
const { normalizeProviderName } = require('../lib/provider-names');
|
|
23
23
|
const { detectGitContext } = require('../lib/git-remote-utils');
|
|
24
|
+
const { readKeychainCredentials } = require('./claude-credentials');
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Validation result
|
|
@@ -133,17 +134,12 @@ function checkMacOsKeychain() {
|
|
|
133
134
|
return { authenticated: false, error: 'Not macOS' };
|
|
134
135
|
}
|
|
135
136
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
encoding: 'utf8',
|
|
140
|
-
stdio: 'pipe',
|
|
141
|
-
timeout: 2000,
|
|
142
|
-
});
|
|
137
|
+
// Reuses the same Keychain read that provisions isolated agent configs, so a
|
|
138
|
+
// green preflight here genuinely predicts runtime auth availability.
|
|
139
|
+
if (readKeychainCredentials()) {
|
|
143
140
|
return { authenticated: true, error: null };
|
|
144
|
-
} catch {
|
|
145
|
-
return { authenticated: false, error: 'No credentials in Keychain' };
|
|
146
141
|
}
|
|
142
|
+
return { authenticated: false, error: 'No credentials in Keychain' };
|
|
147
143
|
}
|
|
148
144
|
|
|
149
145
|
/**
|
|
@@ -523,7 +519,7 @@ function validateGitRequirement() {
|
|
|
523
519
|
* @param {string} options.provider - Provider override
|
|
524
520
|
* @returns {ValidationResult}
|
|
525
521
|
*/
|
|
526
|
-
function runPreflight(options = {}) {
|
|
522
|
+
async function runPreflight(options = {}) {
|
|
527
523
|
const errors = [];
|
|
528
524
|
const warnings = [];
|
|
529
525
|
|
|
@@ -562,8 +558,8 @@ function runPreflight(options = {}) {
|
|
|
562
558
|
if (ProviderClass) {
|
|
563
559
|
const tool = ProviderClass.getRequiredTool();
|
|
564
560
|
|
|
565
|
-
// Check if tool is installed
|
|
566
|
-
if (!commandExists(tool.name)) {
|
|
561
|
+
// Check if tool is installed (providers with no CLI binary set tool.name to null)
|
|
562
|
+
if (tool.name && !commandExists(tool.name)) {
|
|
567
563
|
errors.push(
|
|
568
564
|
formatError(
|
|
569
565
|
`${ProviderClass.displayName} CLI (${tool.name}) not installed`,
|
|
@@ -577,7 +573,7 @@ function runPreflight(options = {}) {
|
|
|
577
573
|
// This ensures we check auth for the actual target, not the current repo
|
|
578
574
|
const targetHost =
|
|
579
575
|
options.targetHost || detectGitContext(options.cwd || process.cwd())?.host;
|
|
580
|
-
const authResult = ProviderClass.checkAuth(targetHost);
|
|
576
|
+
const authResult = await ProviderClass.checkAuth(targetHost);
|
|
581
577
|
if (!authResult.authenticated) {
|
|
582
578
|
errors.push(
|
|
583
579
|
formatError(
|
|
@@ -628,7 +624,7 @@ function runPreflight(options = {}) {
|
|
|
628
624
|
} else if (tool && ProviderClass) {
|
|
629
625
|
// Check provider authentication (abstracted per provider)
|
|
630
626
|
// Pass hostname for multi-instance providers (e.g., GitLab with self-hosted)
|
|
631
|
-
const authResult = ProviderClass.checkAuth(prGitContext?.host);
|
|
627
|
+
const authResult = await ProviderClass.checkAuth(prGitContext?.host);
|
|
632
628
|
if (!authResult.authenticated) {
|
|
633
629
|
errors.push(
|
|
634
630
|
formatError(
|
|
@@ -673,8 +669,8 @@ function runPreflight(options = {}) {
|
|
|
673
669
|
* @param {boolean} options.quiet - Suppress success messages
|
|
674
670
|
* @param {string} options.provider - Provider override
|
|
675
671
|
*/
|
|
676
|
-
function requirePreflight(options = {}) {
|
|
677
|
-
const result = runPreflight(options);
|
|
672
|
+
async function requirePreflight(options = {}) {
|
|
673
|
+
const result = await runPreflight(options);
|
|
678
674
|
|
|
679
675
|
// Print warnings regardless of success
|
|
680
676
|
if (result.warnings.length > 0) {
|
|
@@ -248,16 +248,17 @@ class BaseProvider {
|
|
|
248
248
|
);
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
// minLevel/maxLevel are the user's floor/ceiling cost guardrails, not a hard
|
|
252
|
+
// constraint on template-pinned levels. Clamp an out-of-range level into range
|
|
253
|
+
// rather than throwing: rejecting made a single-level clamp (min === max)
|
|
254
|
+
// unusable with any template that pins a different level, and broke read-only
|
|
255
|
+
// commands like `zeroshot logs` that resolve model specs for display (#162).
|
|
251
256
|
if (maxLevel && rank(level) > rank(maxLevel)) {
|
|
252
|
-
|
|
253
|
-
`Level "${level}" exceeds maxLevel "${maxLevel}" for provider "${this.name}"`
|
|
254
|
-
);
|
|
257
|
+
return maxLevel;
|
|
255
258
|
}
|
|
256
259
|
|
|
257
260
|
if (minLevel && rank(level) < rank(minLevel)) {
|
|
258
|
-
|
|
259
|
-
`Level "${level}" is below minLevel "${minLevel}" for provider "${this.name}"`
|
|
260
|
-
);
|
|
261
|
+
return minLevel;
|
|
261
262
|
}
|
|
262
263
|
|
|
263
264
|
return level;
|
package/src/status-footer.js
CHANGED
|
@@ -118,6 +118,7 @@ class StatusFooter {
|
|
|
118
118
|
this.scrollRegionSet = false;
|
|
119
119
|
this.clusterId = null;
|
|
120
120
|
this.clusterState = 'initializing';
|
|
121
|
+
this.runMode = null;
|
|
121
122
|
this.startTime = Date.now();
|
|
122
123
|
this.messageBus = null; // MessageBus for token usage tracking
|
|
123
124
|
|
|
@@ -392,6 +393,14 @@ class StatusFooter {
|
|
|
392
393
|
this.clusterState = state;
|
|
393
394
|
}
|
|
394
395
|
|
|
396
|
+
/**
|
|
397
|
+
* Set the armed run mode (e.g. 'ship', 'pr', 'worktree', 'docker')
|
|
398
|
+
* @param {string|null} mode
|
|
399
|
+
*/
|
|
400
|
+
setRunMode(mode) {
|
|
401
|
+
this.runMode = mode;
|
|
402
|
+
}
|
|
403
|
+
|
|
395
404
|
/**
|
|
396
405
|
* Register an agent for monitoring
|
|
397
406
|
* @param {AgentState} agentState
|
|
@@ -677,6 +686,10 @@ class StatusFooter {
|
|
|
677
686
|
content += ` ${COLORS.cyan}${COLORS.bold}${shortId}${COLORS.reset} `;
|
|
678
687
|
}
|
|
679
688
|
|
|
689
|
+
if (this.runMode) {
|
|
690
|
+
content += `${COLORS.dim}[${this.runMode}]${COLORS.reset} `;
|
|
691
|
+
}
|
|
692
|
+
|
|
680
693
|
// Fill with border
|
|
681
694
|
const contentLen = this.stripAnsi(content).length;
|
|
682
695
|
const padding = Math.max(0, width - contentLen - 1);
|
|
@@ -879,7 +892,6 @@ class StatusFooter {
|
|
|
879
892
|
* @returns {string}
|
|
880
893
|
*/
|
|
881
894
|
stripAnsi(str) {
|
|
882
|
-
// eslint-disable-next-line no-control-regex
|
|
883
895
|
return str.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
|
|
884
896
|
}
|
|
885
897
|
|
|
@@ -332,7 +332,10 @@ function ensureCompletionHandler(config, options = {}) {
|
|
|
332
332
|
};
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
+
const { formatValidationReport } = require('./report-formatter');
|
|
336
|
+
|
|
335
337
|
module.exports = {
|
|
336
338
|
validateTemplates,
|
|
337
339
|
validateTemplateConfig,
|
|
340
|
+
formatValidationReport,
|
|
338
341
|
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
|
|
3
|
+
function formatEntry({ relativePath, result, changedFiles, lines, suppressed }) {
|
|
4
|
+
if (!result.valid) {
|
|
5
|
+
lines.push(`\n❌ ${relativePath}`);
|
|
6
|
+
for (const error of result.errors) {
|
|
7
|
+
lines.push(` ERROR: ${error}`);
|
|
8
|
+
}
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (result.warnings.length > 0) {
|
|
13
|
+
if (changedFiles === null || changedFiles.has(relativePath)) {
|
|
14
|
+
lines.push(`\n⚠️ ${relativePath}`);
|
|
15
|
+
for (const warning of result.warnings) {
|
|
16
|
+
lines.push(` WARN: ${warning}`);
|
|
17
|
+
}
|
|
18
|
+
} else {
|
|
19
|
+
suppressed.count += result.warnings.length;
|
|
20
|
+
suppressed.fileCount += 1;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
lines.push(`✓ ${relativePath}`);
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function formatValidationReport(report, { changedFiles = null, cwd = process.cwd() } = {}) {
|
|
30
|
+
const lines = [];
|
|
31
|
+
const suppressed = { count: 0, fileCount: 0 };
|
|
32
|
+
let hasErrors = false;
|
|
33
|
+
|
|
34
|
+
for (const { filePath, result } of report.results) {
|
|
35
|
+
const relativePath = path.relative(cwd, filePath);
|
|
36
|
+
if (formatEntry({ relativePath, result, changedFiles, lines, suppressed })) {
|
|
37
|
+
hasErrors = true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (suppressed.count > 0) {
|
|
42
|
+
lines.push(
|
|
43
|
+
`\n${suppressed.count} pre-existing warning(s) across ${suppressed.fileCount} unrelated template(s) — run \`npm run validate:templates\` for detail`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
lines.push(`\n${'='.repeat(60)}`);
|
|
48
|
+
lines.push(`Validated: ${report.validated} templates, Skipped: ${report.skipped} files`);
|
|
49
|
+
|
|
50
|
+
return { lines, hasErrors };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = {
|
|
54
|
+
formatValidationReport,
|
|
55
|
+
};
|
|
@@ -3,6 +3,7 @@ const os = require('os');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
|
|
5
5
|
const { resolveWorktreeRoot } = require('./worktree-tooling-env');
|
|
6
|
+
const { provisionClaudeCredentials } = require('./claude-credentials');
|
|
6
7
|
|
|
7
8
|
const CLAUDE_DIRNAME = '.claude';
|
|
8
9
|
const SETTINGS_BASENAME = 'settings.json';
|
|
@@ -60,15 +61,6 @@ function ensureDir(dirPath) {
|
|
|
60
61
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
function copyIfExists(sourcePath, destPath) {
|
|
64
|
-
if (!fs.existsSync(sourcePath)) {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
ensureDir(path.dirname(destPath));
|
|
69
|
-
fs.copyFileSync(sourcePath, destPath);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
64
|
function resolveRepoClaudeConfig(worktreeRoot) {
|
|
73
65
|
const configDir = path.join(worktreeRoot, CLAUDE_DIRNAME);
|
|
74
66
|
const settingsPath = path.join(configDir, SETTINGS_BASENAME);
|
|
@@ -105,10 +97,7 @@ function prepareClaudeConfigDir(options = {}) {
|
|
|
105
97
|
ensureDir(path.join(overlayDir, 'hooks'));
|
|
106
98
|
ensureDir(path.join(overlayDir, 'projects'));
|
|
107
99
|
|
|
108
|
-
|
|
109
|
-
path.join(sourceDir, '.credentials.json'),
|
|
110
|
-
path.join(overlayDir, '.credentials.json')
|
|
111
|
-
);
|
|
100
|
+
provisionClaudeCredentials({ sourceDir, destDir: overlayDir });
|
|
112
101
|
|
|
113
102
|
const sourceSettings = readJsonIfExists(path.join(sourceDir, SETTINGS_BASENAME)) || {};
|
|
114
103
|
const repoSettings = readJsonIfExists(repoConfig.settingsPath) || {};
|
package/task-lib/runner.js
CHANGED