@joynt/foundry 0.0.0-stage → 0.1.50
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/dist/backends/command.d.ts +37 -0
- package/dist/backends/command.d.ts.map +1 -0
- package/dist/backends/command.js +79 -0
- package/dist/backends/command.js.map +1 -0
- package/dist/backends/index.d.ts +9 -0
- package/dist/backends/index.d.ts.map +1 -0
- package/dist/backends/index.js +37 -0
- package/dist/backends/index.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +191 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/action.d.ts +6 -0
- package/dist/commands/action.d.ts.map +1 -0
- package/dist/commands/action.js +171 -0
- package/dist/commands/action.js.map +1 -0
- package/dist/commands/cancelled.d.ts +5 -0
- package/dist/commands/cancelled.d.ts.map +1 -0
- package/dist/commands/cancelled.js +107 -0
- package/dist/commands/cancelled.js.map +1 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +323 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/preview.d.ts +4 -0
- package/dist/commands/preview.d.ts.map +1 -0
- package/dist/commands/preview.js +108 -0
- package/dist/commands/preview.js.map +1 -0
- package/dist/commands/release.d.ts +2 -0
- package/dist/commands/release.d.ts.map +1 -0
- package/dist/commands/release.js +89 -0
- package/dist/commands/release.js.map +1 -0
- package/dist/commands/report.d.ts +14 -0
- package/dist/commands/report.d.ts.map +1 -0
- package/dist/commands/report.js +91 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/reset.d.ts +4 -0
- package/dist/commands/reset.d.ts.map +1 -0
- package/dist/commands/reset.js +155 -0
- package/dist/commands/reset.js.map +1 -0
- package/dist/commands/review.d.ts +2 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +73 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/setup-bot.d.ts +31 -0
- package/dist/commands/setup-bot.d.ts.map +1 -0
- package/dist/commands/setup-bot.js +429 -0
- package/dist/commands/setup-bot.js.map +1 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +77 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +147 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/agent-output.d.ts +49 -0
- package/dist/lib/agent-output.d.ts.map +1 -0
- package/dist/lib/agent-output.js +256 -0
- package/dist/lib/agent-output.js.map +1 -0
- package/dist/lib/agent-report.d.ts +107 -0
- package/dist/lib/agent-report.d.ts.map +1 -0
- package/dist/lib/agent-report.js +233 -0
- package/dist/lib/agent-report.js.map +1 -0
- package/dist/lib/authorize.d.ts +3 -0
- package/dist/lib/authorize.d.ts.map +1 -0
- package/dist/lib/authorize.js +67 -0
- package/dist/lib/authorize.js.map +1 -0
- package/dist/lib/branches.d.ts +22 -0
- package/dist/lib/branches.d.ts.map +1 -0
- package/dist/lib/branches.js +82 -0
- package/dist/lib/branches.js.map +1 -0
- package/dist/lib/claim.d.ts +22 -0
- package/dist/lib/claim.d.ts.map +1 -0
- package/dist/lib/claim.js +129 -0
- package/dist/lib/claim.js.map +1 -0
- package/dist/lib/completion.d.ts +5 -0
- package/dist/lib/completion.d.ts.map +1 -0
- package/dist/lib/completion.js +63 -0
- package/dist/lib/completion.js.map +1 -0
- package/dist/lib/deployments.d.ts +21 -0
- package/dist/lib/deployments.d.ts.map +1 -0
- package/dist/lib/deployments.js +104 -0
- package/dist/lib/deployments.js.map +1 -0
- package/dist/lib/event-handler.d.ts +74 -0
- package/dist/lib/event-handler.d.ts.map +1 -0
- package/dist/lib/event-handler.js +1002 -0
- package/dist/lib/event-handler.js.map +1 -0
- package/dist/lib/events.d.ts +72 -0
- package/dist/lib/events.d.ts.map +1 -0
- package/dist/lib/events.js +3 -0
- package/dist/lib/events.js.map +1 -0
- package/dist/lib/gh-cli-client.d.ts +46 -0
- package/dist/lib/gh-cli-client.d.ts.map +1 -0
- package/dist/lib/gh-cli-client.js +260 -0
- package/dist/lib/gh-cli-client.js.map +1 -0
- package/dist/lib/git.d.ts +42 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +247 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/github-client.d.ts +98 -0
- package/dist/lib/github-client.d.ts.map +1 -0
- package/dist/lib/github-client.js +3 -0
- package/dist/lib/github-client.js.map +1 -0
- package/dist/lib/github.d.ts +82 -0
- package/dist/lib/github.d.ts.map +1 -0
- package/dist/lib/github.js +202 -0
- package/dist/lib/github.js.map +1 -0
- package/dist/lib/issue-file.d.ts +15 -0
- package/dist/lib/issue-file.d.ts.map +1 -0
- package/dist/lib/issue-file.js +84 -0
- package/dist/lib/issue-file.js.map +1 -0
- package/dist/lib/labels.d.ts +14 -0
- package/dist/lib/labels.d.ts.map +1 -0
- package/dist/lib/labels.js +70 -0
- package/dist/lib/labels.js.map +1 -0
- package/dist/lib/log.d.ts +11 -0
- package/dist/lib/log.d.ts.map +1 -0
- package/dist/lib/log.js +53 -0
- package/dist/lib/log.js.map +1 -0
- package/dist/lib/octokit-client.d.ts +112 -0
- package/dist/lib/octokit-client.d.ts.map +1 -0
- package/dist/lib/octokit-client.js +507 -0
- package/dist/lib/octokit-client.js.map +1 -0
- package/dist/lib/preview-verify.d.ts +48 -0
- package/dist/lib/preview-verify.d.ts.map +1 -0
- package/dist/lib/preview-verify.js +148 -0
- package/dist/lib/preview-verify.js.map +1 -0
- package/dist/lib/preview.d.ts +33 -0
- package/dist/lib/preview.d.ts.map +1 -0
- package/dist/lib/preview.js +308 -0
- package/dist/lib/preview.js.map +1 -0
- package/dist/lib/reconcile.d.ts +14 -0
- package/dist/lib/reconcile.d.ts.map +1 -0
- package/dist/lib/reconcile.js +114 -0
- package/dist/lib/reconcile.js.map +1 -0
- package/dist/lib/review.d.ts +38 -0
- package/dist/lib/review.d.ts.map +1 -0
- package/dist/lib/review.js +284 -0
- package/dist/lib/review.js.map +1 -0
- package/dist/lib/stale-prs.d.ts +31 -0
- package/dist/lib/stale-prs.d.ts.map +1 -0
- package/dist/lib/stale-prs.js +161 -0
- package/dist/lib/stale-prs.js.map +1 -0
- package/dist/lib/state-store.d.ts +23 -0
- package/dist/lib/state-store.d.ts.map +1 -0
- package/dist/lib/state-store.js +3 -0
- package/dist/lib/state-store.js.map +1 -0
- package/dist/lib/state.d.ts +23 -0
- package/dist/lib/state.d.ts.map +1 -0
- package/dist/lib/state.js +57 -0
- package/dist/lib/state.js.map +1 -0
- package/dist/lib/status-comment.d.ts +14 -0
- package/dist/lib/status-comment.d.ts.map +1 -0
- package/dist/lib/status-comment.js +195 -0
- package/dist/lib/status-comment.js.map +1 -0
- package/dist/lib/stores/file-store.d.ts +22 -0
- package/dist/lib/stores/file-store.d.ts.map +1 -0
- package/dist/lib/stores/file-store.js +172 -0
- package/dist/lib/stores/file-store.js.map +1 -0
- package/dist/lib/stores/index.d.ts +13 -0
- package/dist/lib/stores/index.d.ts.map +1 -0
- package/dist/lib/stores/index.js +20 -0
- package/dist/lib/stores/index.js.map +1 -0
- package/dist/lib/task-recovery.d.ts +22 -0
- package/dist/lib/task-recovery.d.ts.map +1 -0
- package/dist/lib/task-recovery.js +172 -0
- package/dist/lib/task-recovery.js.map +1 -0
- package/dist/lib/templates.d.ts +8 -0
- package/dist/lib/templates.d.ts.map +1 -0
- package/dist/lib/templates.js +58 -0
- package/dist/lib/templates.js.map +1 -0
- package/dist/lib/thread.d.ts +13 -0
- package/dist/lib/thread.d.ts.map +1 -0
- package/dist/lib/thread.js +75 -0
- package/dist/lib/thread.js.map +1 -0
- package/dist/lib/verify.d.ts +31 -0
- package/dist/lib/verify.d.ts.map +1 -0
- package/dist/lib/verify.js +90 -0
- package/dist/lib/verify.js.map +1 -0
- package/dist/lib/version.d.ts +9 -0
- package/dist/lib/version.d.ts.map +1 -0
- package/dist/lib/version.js +134 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/lib/webhook.d.ts +39 -0
- package/dist/lib/webhook.d.ts.map +1 -0
- package/dist/lib/webhook.js +248 -0
- package/dist/lib/webhook.js.map +1 -0
- package/dist/lib/worker.d.ts +42 -0
- package/dist/lib/worker.d.ts.map +1 -0
- package/dist/lib/worker.js +9 -0
- package/dist/lib/worker.js.map +1 -0
- package/dist/lib/workers/index.d.ts +11 -0
- package/dist/lib/workers/index.d.ts.map +1 -0
- package/dist/lib/workers/index.js +18 -0
- package/dist/lib/workers/index.js.map +1 -0
- package/dist/lib/workers/subprocess.d.ts +8 -0
- package/dist/lib/workers/subprocess.d.ts.map +1 -0
- package/dist/lib/workers/subprocess.js +105 -0
- package/dist/lib/workers/subprocess.js.map +1 -0
- package/dist/lib/worktree.d.ts +21 -0
- package/dist/lib/worktree.d.ts.map +1 -0
- package/dist/lib/worktree.js +113 -0
- package/dist/lib/worktree.js.map +1 -0
- package/dist/types.d.ts +201 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +44 -4
- package/templates/foundry.yml +121 -0
- package/templates/vercel-preview-url.sh +61 -0
- package/README.md +0 -3
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.preserveWork = preserveWork;
|
|
37
|
+
exports.ensureWorktree = ensureWorktree;
|
|
38
|
+
/**
|
|
39
|
+
* Worktree lifecycle across jobs.
|
|
40
|
+
*
|
|
41
|
+
* A task's worktree lives only as long as the job that created it. When a job
|
|
42
|
+
* ends with the agent waiting on a human, the branch is pushed so nothing is
|
|
43
|
+
* lost; when a later job resumes the task, the worktree is recreated from the
|
|
44
|
+
* pushed branch. Both functions are idempotent and safe to call when there is
|
|
45
|
+
* nothing to do.
|
|
46
|
+
*/
|
|
47
|
+
const fs = __importStar(require("node:fs"));
|
|
48
|
+
const git = __importStar(require("./git.js"));
|
|
49
|
+
const log = __importStar(require("./log.js"));
|
|
50
|
+
const branches_js_1 = require("./branches.js");
|
|
51
|
+
/**
|
|
52
|
+
* Make the agent's work survive the end of this job: commit anything
|
|
53
|
+
* uncommitted, then push the branch if it has commits beyond its base.
|
|
54
|
+
* Never throws; a failure is logged and reported in the result so the
|
|
55
|
+
* status comment can say so.
|
|
56
|
+
*/
|
|
57
|
+
function preserveWork(config, task, repoDir, reason) {
|
|
58
|
+
const result = { committed: false, pushed: false };
|
|
59
|
+
const wt = task.worktree;
|
|
60
|
+
if (!fs.existsSync(wt))
|
|
61
|
+
return result;
|
|
62
|
+
try {
|
|
63
|
+
if (git.isDirty(wt)) {
|
|
64
|
+
git.commitAll(`chore(foundry): work in progress (${reason})`, wt);
|
|
65
|
+
result.committed = true;
|
|
66
|
+
log.info(`#${task.issue}: committed work in progress before pausing.`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
log.warn(`#${task.issue}: could not commit work in progress: ${err.message}`);
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const base = (0, branches_js_1.resolveBaseBranch)(config, repoDir);
|
|
74
|
+
if (git.countCommits(`${base}..HEAD`, wt) > 0) {
|
|
75
|
+
git.push(task.branch, wt, true);
|
|
76
|
+
result.pushed = true;
|
|
77
|
+
log.info(`#${task.issue}: pushed ${task.branch}.`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
log.warn(`#${task.issue}: could not push ${task.branch}: ${err.message}`);
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Make sure the task's worktree exists before an agent is launched into it.
|
|
87
|
+
* In a fresh job it is recreated from the pushed branch; if the branch was
|
|
88
|
+
* never pushed, from the base branch.
|
|
89
|
+
*/
|
|
90
|
+
function ensureWorktree(config, task, repoDir) {
|
|
91
|
+
if (git.worktreeExists(task.worktree, repoDir) && fs.existsSync(task.worktree))
|
|
92
|
+
return;
|
|
93
|
+
try {
|
|
94
|
+
git.fetchAll(repoDir);
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
log.warn(`Fetch failed before recreating worktree: ${err.message}`);
|
|
98
|
+
}
|
|
99
|
+
// A stale registration with no directory (job ended, path gone) must be pruned first.
|
|
100
|
+
git.pruneWorktrees(repoDir);
|
|
101
|
+
if (git.remoteBranchExists(task.branch, repoDir)) {
|
|
102
|
+
git.addWorktreeTracking(task.worktree, task.branch, repoDir);
|
|
103
|
+
log.info(`#${task.issue}: recreated worktree from origin/${task.branch}.`);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const base = (0, branches_js_1.resolveBaseBranch)(config, repoDir);
|
|
107
|
+
if (git.branchExists(task.branch, repoDir))
|
|
108
|
+
git.deleteBranch(task.branch, repoDir);
|
|
109
|
+
git.addWorktree(task.worktree, task.branch, base, repoDir);
|
|
110
|
+
log.info(`#${task.issue}: created worktree for ${task.branch} from ${base} (branch was never pushed).`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=worktree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.js","sourceRoot":"","sources":["../../src/lib/worktree.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Joynt Foundry — Core type definitions
|
|
3
|
+
*/
|
|
4
|
+
export interface AgentBackendDef {
|
|
5
|
+
type: 'command';
|
|
6
|
+
/** Use a command Foundry maintains instead of copying one into config, where it would drift. */
|
|
7
|
+
preset?: 'claude-code';
|
|
8
|
+
/** Required unless `preset` is set. Overrides the preset's command when both are given. */
|
|
9
|
+
command?: string;
|
|
10
|
+
env?: Record<string, string>;
|
|
11
|
+
}
|
|
12
|
+
export interface FoundryConfig {
|
|
13
|
+
repo: string;
|
|
14
|
+
/**
|
|
15
|
+
* State labels. Foundry owns them: exactly one is on a tracked issue at a
|
|
16
|
+
* time and only Foundry adds or removes them. Humans never set a state
|
|
17
|
+
* label to make something happen; they comment a command.
|
|
18
|
+
*/
|
|
19
|
+
labels: {
|
|
20
|
+
in_progress: string;
|
|
21
|
+
done: string;
|
|
22
|
+
ready_for_review: string;
|
|
23
|
+
waiting_for_input: string;
|
|
24
|
+
failed: string;
|
|
25
|
+
plan_review: string;
|
|
26
|
+
};
|
|
27
|
+
mode_labels?: {
|
|
28
|
+
plan: string;
|
|
29
|
+
auto: string;
|
|
30
|
+
default: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* The repo's long-lived branches, by name. Feature branches are cut from
|
|
34
|
+
* and merged into `integration`; `production` is what it is promoted to.
|
|
35
|
+
* Never assumed: onboarding must not require renaming a branch that
|
|
36
|
+
* deploy tooling already keys on.
|
|
37
|
+
*/
|
|
38
|
+
branches: {
|
|
39
|
+
integration: string;
|
|
40
|
+
production: string;
|
|
41
|
+
};
|
|
42
|
+
branch_template: string;
|
|
43
|
+
worktree_base: string;
|
|
44
|
+
max_input_rounds: number;
|
|
45
|
+
/** Passed to the agent as FOUNDRY_MAX_TURNS; the preset uses --max-turns. Default 150. */
|
|
46
|
+
agent_max_turns?: number;
|
|
47
|
+
/** An in-progress issue with no status update for this long is marked failed by `foundry reconcile`. Default 120. */
|
|
48
|
+
reconcile_stale_minutes?: number;
|
|
49
|
+
verify: string[];
|
|
50
|
+
/** Run against the live preview (FOUNDRY_PREVIEW_URL) before ready-for-review. Default []. */
|
|
51
|
+
verify_preview?: string[];
|
|
52
|
+
integration_rebuild: string;
|
|
53
|
+
/**
|
|
54
|
+
* The three commands, the only comments that start or stop work. Every
|
|
55
|
+
* other comment is context: it reaches the agent through the issue file
|
|
56
|
+
* on the next launch, and never starts a job.
|
|
57
|
+
*/
|
|
58
|
+
comment_triggers: {
|
|
59
|
+
go: string;
|
|
60
|
+
restart: string;
|
|
61
|
+
stop: string;
|
|
62
|
+
};
|
|
63
|
+
version_sources: string[];
|
|
64
|
+
tag_prefix: string;
|
|
65
|
+
default_agent_backend: string;
|
|
66
|
+
agent_backends: Record<string, AgentBackendDef>;
|
|
67
|
+
agent_label_map?: Record<string, string>;
|
|
68
|
+
github_backend: 'gh-cli' | 'octokit';
|
|
69
|
+
preview?: {
|
|
70
|
+
mode: 'template' | 'provider';
|
|
71
|
+
url_template?: string;
|
|
72
|
+
up_command?: string;
|
|
73
|
+
down_command?: string;
|
|
74
|
+
comment?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Also preview any pull request whose base branch is listed here, task or
|
|
77
|
+
* not: up on open/reopen, redeploy on push, down on close. For the
|
|
78
|
+
* promotion PR from `integration` into `main`.
|
|
79
|
+
*/
|
|
80
|
+
pull_request_bases?: string[];
|
|
81
|
+
/** How long to wait for the preview URL to answer before verify_preview runs. Default 300. */
|
|
82
|
+
ready_timeout_seconds?: number;
|
|
83
|
+
/**
|
|
84
|
+
* Text the preview's body must contain before it counts as up, with
|
|
85
|
+
* {sha}, {branch}, {pr_number} expanded. Without it a redeploy is
|
|
86
|
+
* verified against the previous version still being served.
|
|
87
|
+
*/
|
|
88
|
+
ready_match?: string;
|
|
89
|
+
};
|
|
90
|
+
review?: {
|
|
91
|
+
/** On an approving review: rebase onto the base, wait for checks, merge (rebase method). Default true. */
|
|
92
|
+
merge_on_approval?: boolean;
|
|
93
|
+
/** How long to wait for the PR's checks before giving up. Default 900. */
|
|
94
|
+
checks_timeout_seconds?: number;
|
|
95
|
+
};
|
|
96
|
+
github_deployments?: {
|
|
97
|
+
enabled: boolean;
|
|
98
|
+
environment?: string;
|
|
99
|
+
production?: boolean;
|
|
100
|
+
auto_inactive?: boolean;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export type TaskStatus = 'claimed' | 'agent-running' | 'verifying' | 'pr-open' | 'reviewing' | 'waiting-for-input' | 'resuming' | 'pr-changes-requested' | 'plan-review' | 'done' | 'failed' | 'stopped';
|
|
104
|
+
export interface TaskState {
|
|
105
|
+
issue: number;
|
|
106
|
+
title: string;
|
|
107
|
+
repo: string;
|
|
108
|
+
branch: string;
|
|
109
|
+
worktree: string;
|
|
110
|
+
tmux_session: string;
|
|
111
|
+
pr_url?: string;
|
|
112
|
+
pr_number?: number;
|
|
113
|
+
agent_backend: string;
|
|
114
|
+
agent_command?: string;
|
|
115
|
+
status: TaskStatus;
|
|
116
|
+
claimed_at: string;
|
|
117
|
+
updated_at: string;
|
|
118
|
+
runner_id: string;
|
|
119
|
+
session_id?: string;
|
|
120
|
+
permission_mode?: string;
|
|
121
|
+
input_request_count?: number;
|
|
122
|
+
last_agent_message?: string;
|
|
123
|
+
/** Usage of the most recent agent run, from the transcript's result event. */
|
|
124
|
+
last_run?: {
|
|
125
|
+
cost_usd?: number;
|
|
126
|
+
turns?: number;
|
|
127
|
+
duration_ms?: number;
|
|
128
|
+
};
|
|
129
|
+
/** The issue body at claim time; written to FOUNDRY_ISSUE_FILE on every launch. */
|
|
130
|
+
issue_body?: string;
|
|
131
|
+
preview_url?: string;
|
|
132
|
+
deployment_id?: number;
|
|
133
|
+
}
|
|
134
|
+
export interface RunnerState {
|
|
135
|
+
runner_id: string;
|
|
136
|
+
repo: string;
|
|
137
|
+
tasks: Record<number, TaskState>;
|
|
138
|
+
}
|
|
139
|
+
export interface AgentLaunchParams {
|
|
140
|
+
worktree: string;
|
|
141
|
+
issue_url: string;
|
|
142
|
+
issue_number: number;
|
|
143
|
+
repo: string;
|
|
144
|
+
title: string;
|
|
145
|
+
body: string;
|
|
146
|
+
labels: string[];
|
|
147
|
+
log_dir: string;
|
|
148
|
+
state_dir: string;
|
|
149
|
+
permission_mode: string;
|
|
150
|
+
}
|
|
151
|
+
export interface AgentBackend {
|
|
152
|
+
name: string;
|
|
153
|
+
resolveCommand(params: AgentLaunchParams): string;
|
|
154
|
+
resolveEnv(params: AgentLaunchParams): Record<string, string>;
|
|
155
|
+
}
|
|
156
|
+
export interface GitHubIssue {
|
|
157
|
+
number: number;
|
|
158
|
+
title: string;
|
|
159
|
+
body: string | null;
|
|
160
|
+
labels: Array<{
|
|
161
|
+
name: string;
|
|
162
|
+
}>;
|
|
163
|
+
html_url: string;
|
|
164
|
+
state: string;
|
|
165
|
+
}
|
|
166
|
+
export interface GitHubComment {
|
|
167
|
+
id: number;
|
|
168
|
+
body: string;
|
|
169
|
+
user: {
|
|
170
|
+
login: string;
|
|
171
|
+
} | null;
|
|
172
|
+
created_at: string;
|
|
173
|
+
}
|
|
174
|
+
export type AgentOutcomeType = 'completed' | 'needs-input' | 'plan-completed' | 'blocked' | 'errored';
|
|
175
|
+
export interface AskUserQuestionOption {
|
|
176
|
+
label: string;
|
|
177
|
+
description: string;
|
|
178
|
+
}
|
|
179
|
+
export interface AskUserQuestionData {
|
|
180
|
+
question: string;
|
|
181
|
+
options: AskUserQuestionOption[];
|
|
182
|
+
multiSelect: boolean;
|
|
183
|
+
}
|
|
184
|
+
export interface AgentOutcome {
|
|
185
|
+
type: AgentOutcomeType;
|
|
186
|
+
session_id: string | null;
|
|
187
|
+
/** Text to show a human: the report summary, the plan, or a failure explanation. Never branched on. */
|
|
188
|
+
final_message: string | null;
|
|
189
|
+
ask_user_questions: AskUserQuestionData[] | null;
|
|
190
|
+
/** The agent's typed report, when it submitted a valid one. */
|
|
191
|
+
report?: import('./lib/agent-report.js').AgentReport | null;
|
|
192
|
+
/** Why the task failed or is blocked, for `errored` and `blocked`. */
|
|
193
|
+
reason?: string;
|
|
194
|
+
/** From the result event when present. */
|
|
195
|
+
usage?: {
|
|
196
|
+
cost_usd?: number;
|
|
197
|
+
turns?: number;
|
|
198
|
+
duration_ms?: number;
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;GAEG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@joynt/foundry",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.50",
|
|
4
|
+
"description": "Developer automation: GitHub Issues → agent sessions → validated PRs",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"bin": {
|
|
7
|
+
"foundry": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"templates"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:watch": "vitest",
|
|
19
|
+
"prepublishOnly": "npm run build",
|
|
20
|
+
"dev": "tsc --watch"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"registry": "https://registry.npmjs.org",
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/joyntshops/foundry.git",
|
|
29
|
+
"directory": "packages/foundry"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@octokit/auth-app": "^8.2.0",
|
|
36
|
+
"@octokit/rest": "^22.0.1",
|
|
37
|
+
"chalk": "^5.4.1",
|
|
38
|
+
"commander": "^12.1.0",
|
|
39
|
+
"yaml": "^2.6.1"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^20.17.0",
|
|
43
|
+
"typescript": "^5.7.0",
|
|
44
|
+
"vitest": "^4.0.18"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Foundry: GitHub Issues → coding agent → verified pull request.
|
|
2
|
+
#
|
|
3
|
+
# Every run is one GitHub event. Foundry reads the event, rebuilds task state
|
|
4
|
+
# from the issue (labels + claim comment), performs the transition, and exits.
|
|
5
|
+
# State lives on GitHub, not on any machine. Humans drive it with three issue
|
|
6
|
+
# comments: `@foundry go`, `@foundry restart`, `@foundry stop`.
|
|
7
|
+
#
|
|
8
|
+
# GitHub starts issue_comment, schedule, and workflow_dispatch runs from the
|
|
9
|
+
# DEFAULT branch, so this file and .joynt-foundry.yml govern those runs only
|
|
10
|
+
# once they are on it. `gh workflow run foundry.yml --ref <branch> ...` runs
|
|
11
|
+
# the copy on another branch.
|
|
12
|
+
#
|
|
13
|
+
# Secrets (repository or organization level):
|
|
14
|
+
# CLAUDE_CODE_OAUTH_TOKEN required. From `claude setup-token`; bills that Claude plan.
|
|
15
|
+
# FOUNDRY_APP_ID, FOUNDRY_APP_PRIVATE_KEY a GitHub App installed on this repo (`foundry setup-bot`).
|
|
16
|
+
# With it, Foundry's comments and PRs carry the App's name and
|
|
17
|
+
# its PRs trigger the repo's other workflows. Without it the
|
|
18
|
+
# job's built-in token is used and agent PRs get no CI.
|
|
19
|
+
name: Foundry
|
|
20
|
+
|
|
21
|
+
on:
|
|
22
|
+
issue_comment:
|
|
23
|
+
types: [created]
|
|
24
|
+
pull_request:
|
|
25
|
+
types: [opened, synchronize, reopened, closed]
|
|
26
|
+
pull_request_review:
|
|
27
|
+
types: [submitted]
|
|
28
|
+
# Foundry dispatches itself to hand a command to another issue's job (e.g.
|
|
29
|
+
# sending an agent to rebase after integration moved). Also how to run the
|
|
30
|
+
# workflow from a non-default branch: --ref <branch>.
|
|
31
|
+
workflow_dispatch:
|
|
32
|
+
inputs:
|
|
33
|
+
issue:
|
|
34
|
+
description: Issue number
|
|
35
|
+
required: true
|
|
36
|
+
type: string
|
|
37
|
+
command:
|
|
38
|
+
description: "Foundry command: go | restart | stop"
|
|
39
|
+
required: true
|
|
40
|
+
type: string
|
|
41
|
+
message:
|
|
42
|
+
description: Message for the agent
|
|
43
|
+
required: false
|
|
44
|
+
type: string
|
|
45
|
+
default: ""
|
|
46
|
+
# Reconcile: an in-progress issue whose run died without cleanup is marked
|
|
47
|
+
# failed. Each run costs about 30 seconds of runner time.
|
|
48
|
+
schedule:
|
|
49
|
+
- cron: '0 */6 * * *'
|
|
50
|
+
|
|
51
|
+
permissions:
|
|
52
|
+
contents: write # push branches
|
|
53
|
+
issues: write # labels, claim/status comments
|
|
54
|
+
pull-requests: write # open PRs, PR comments
|
|
55
|
+
checks: write # verification results as a check run on the PR
|
|
56
|
+
actions: write # workflow_dispatch to itself; preview up/down
|
|
57
|
+
deployments: write # GitHub Deployment records for previews
|
|
58
|
+
|
|
59
|
+
jobs:
|
|
60
|
+
foundry:
|
|
61
|
+
# Comments: only `@foundry` commands, only on issues (not PRs), only from
|
|
62
|
+
# people associated with the repo; the job then requires write permission
|
|
63
|
+
# before acting. Plain comments and labels never start a run; the agent
|
|
64
|
+
# reads them at its next launch. PR events: merges, and PRs into the
|
|
65
|
+
# production branch (previewed when preview.pull_request_bases lists it).
|
|
66
|
+
if: >-
|
|
67
|
+
(github.event_name == 'issue_comment' &&
|
|
68
|
+
!endsWith(github.event.comment.user.login, '[bot]') &&
|
|
69
|
+
!github.event.issue.pull_request &&
|
|
70
|
+
startsWith(github.event.comment.body, '@foundry') &&
|
|
71
|
+
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) ||
|
|
72
|
+
(github.event_name == 'pull_request' &&
|
|
73
|
+
(github.event.pull_request.merged == true ||
|
|
74
|
+
github.event.pull_request.base.ref == '__PRODUCTION_BRANCH__')) ||
|
|
75
|
+
(github.event_name == 'pull_request_review' &&
|
|
76
|
+
(github.event.review.state == 'changes_requested' || github.event.review.state == 'approved')) ||
|
|
77
|
+
github.event_name == 'workflow_dispatch' ||
|
|
78
|
+
github.event_name == 'schedule'
|
|
79
|
+
runs-on: ubuntu-latest
|
|
80
|
+
timeout-minutes: 90
|
|
81
|
+
|
|
82
|
+
# One job at a time per issue, declared on the job so runs filtered out by
|
|
83
|
+
# the `if:` above never enter the group. `@foundry stop` cancels the issue's
|
|
84
|
+
# running job; everything else queues.
|
|
85
|
+
concurrency:
|
|
86
|
+
group: foundry-${{ github.event.inputs.issue || github.event.issue.number || github.event.pull_request.number || github.event_name }}
|
|
87
|
+
cancel-in-progress: ${{ github.event_name == 'issue_comment' && contains(github.event.comment.body, '@foundry stop') }}
|
|
88
|
+
|
|
89
|
+
env:
|
|
90
|
+
FOUNDRY_APP_ID: ${{ secrets.FOUNDRY_APP_ID }} # secrets aren't readable in step `if:`
|
|
91
|
+
|
|
92
|
+
steps:
|
|
93
|
+
- name: Mint GitHub App token
|
|
94
|
+
id: app
|
|
95
|
+
if: env.FOUNDRY_APP_ID != ''
|
|
96
|
+
uses: actions/create-github-app-token@v1
|
|
97
|
+
with:
|
|
98
|
+
app-id: ${{ secrets.FOUNDRY_APP_ID }}
|
|
99
|
+
private-key: ${{ secrets.FOUNDRY_APP_PRIVATE_KEY }}
|
|
100
|
+
|
|
101
|
+
# Full history so the integration branch exists for worktrees and diffs.
|
|
102
|
+
- uses: actions/checkout@v4
|
|
103
|
+
with:
|
|
104
|
+
token: ${{ steps.app.outputs.token || github.token }}
|
|
105
|
+
fetch-depth: 0
|
|
106
|
+
|
|
107
|
+
- name: Foundry
|
|
108
|
+
id: foundry
|
|
109
|
+
uses: joyntshops/foundry@main
|
|
110
|
+
with:
|
|
111
|
+
github-token: ${{ steps.app.outputs.token || github.token }}
|
|
112
|
+
claude-code-oauth-token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
113
|
+
|
|
114
|
+
- name: Keep the agent transcript
|
|
115
|
+
if: always() && steps.foundry.outputs.log-dir != ''
|
|
116
|
+
uses: actions/upload-artifact@v4
|
|
117
|
+
with:
|
|
118
|
+
name: foundry-issue-${{ steps.foundry.outputs.issue }}-run-${{ github.run_id }}
|
|
119
|
+
path: ${{ steps.foundry.outputs.log-dir }}
|
|
120
|
+
if-no-files-found: ignore
|
|
121
|
+
retention-days: 30
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Foundry preview `up_command` for a repo that Vercel's GitHub integration
|
|
3
|
+
# already deploys: print the URL of the Vercel deployment that built
|
|
4
|
+
# $FOUNDRY_SHA, waiting for it to succeed. No Vercel credential is needed.
|
|
5
|
+
#
|
|
6
|
+
# Vercel records each deployment in GitHub's Deployments API as
|
|
7
|
+
# "Preview – <project>" or "Production – <project>", with the deployment URL in
|
|
8
|
+
# `environment_url`. Reading that needs only the job's GitHub token. Each
|
|
9
|
+
# commit gets its own URL, so a relaunched task is never verified against a
|
|
10
|
+
# stale page, and no `down_command` is needed: Vercel expires previews itself.
|
|
11
|
+
#
|
|
12
|
+
# When the integration branch is the project's production branch, its commits
|
|
13
|
+
# get a Production deployment, whose generated hostname may not serve a
|
|
14
|
+
# host-keyed app correctly. For those, the project's production alias is
|
|
15
|
+
# printed instead (VERCEL_PRODUCTION_URL); it serves exactly this commit once
|
|
16
|
+
# the deployment has succeeded. This is the case for a promotion PR.
|
|
17
|
+
#
|
|
18
|
+
# Env: FOUNDRY_SHA and GH_TOKEN (set by Foundry's job), GITHUB_REPOSITORY (set by Actions),
|
|
19
|
+
# VERCEL_PREVIEW_PROJECT (required: the Vercel project name),
|
|
20
|
+
# VERCEL_PRODUCTION_URL (required only if the integration branch is the project's production branch),
|
|
21
|
+
# VERCEL_PREVIEW_TIMEOUT (seconds, default 900).
|
|
22
|
+
set -u
|
|
23
|
+
|
|
24
|
+
REPO="${GITHUB_REPOSITORY:?GITHUB_REPOSITORY is not set}"
|
|
25
|
+
PROJECT="${VERCEL_PREVIEW_PROJECT:?VERCEL_PREVIEW_PROJECT is not set}"
|
|
26
|
+
PRODUCTION_URL="${VERCEL_PRODUCTION_URL:-}"
|
|
27
|
+
SHA="${FOUNDRY_SHA:?FOUNDRY_SHA is not set}"
|
|
28
|
+
DEADLINE=$((SECONDS + ${VERCEL_PREVIEW_TIMEOUT:-900}))
|
|
29
|
+
|
|
30
|
+
while [ "$SECONDS" -lt "$DEADLINE" ]; do
|
|
31
|
+
# Deployments for this commit, newest first; keep the one for our project.
|
|
32
|
+
found=$(gh api "repos/$REPO/deployments?sha=$SHA&per_page=20" \
|
|
33
|
+
--jq "[.[] | select(.environment | endswith(\"– $PROJECT\"))] | sort_by(.created_at) | last | select(. != null) | \"\(.id) \(.environment)\"" 2>/dev/null || true)
|
|
34
|
+
if [ -z "$found" ]; then
|
|
35
|
+
echo "Waiting for Vercel: no deployment of ${SHA:0:7} on $PROJECT recorded yet" >&2
|
|
36
|
+
sleep 20; continue
|
|
37
|
+
fi
|
|
38
|
+
dep="${found%% *}"; environment="${found#* }"
|
|
39
|
+
# Latest status wins: success carries the URL; failure/error/inactive end it.
|
|
40
|
+
status=$(gh api "repos/$REPO/deployments/$dep/statuses?per_page=1" \
|
|
41
|
+
--jq '.[0] | "\(.state) \(.environment_url // .target_url // "")"' 2>/dev/null || echo "unknown ")
|
|
42
|
+
state="${status%% *}"; url="${status#* }"
|
|
43
|
+
case "$state" in
|
|
44
|
+
success)
|
|
45
|
+
case "$environment" in
|
|
46
|
+
Production*)
|
|
47
|
+
if [ -n "$PRODUCTION_URL" ]; then
|
|
48
|
+
echo "Deployment $dep of ${SHA:0:7} is the production deployment of $PROJECT; using its alias $PRODUCTION_URL" >&2
|
|
49
|
+
echo "$PRODUCTION_URL"; exit 0
|
|
50
|
+
fi ;;
|
|
51
|
+
esac
|
|
52
|
+
[ -n "$url" ] && { echo "$url"; exit 0; }
|
|
53
|
+
echo "Deployment $dep succeeded but has no URL" >&2; exit 1 ;;
|
|
54
|
+
failure|error|inactive)
|
|
55
|
+
echo "Vercel deployment of ${SHA:0:7} on $PROJECT ended in '$state' (deployment $dep)" >&2; exit 1 ;;
|
|
56
|
+
*)
|
|
57
|
+
echo "Waiting for Vercel: deployment $dep of ${SHA:0:7} on $PROJECT is '${state:-pending}'" >&2; sleep 20 ;;
|
|
58
|
+
esac
|
|
59
|
+
done
|
|
60
|
+
echo "Timed out waiting for the Vercel deployment of ${SHA:0:7} on $PROJECT" >&2
|
|
61
|
+
exit 1
|
package/README.md
DELETED