@almightygpt/core 0.2.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/dist/adapters/claude.d.ts +31 -0
- package/dist/adapters/claude.d.ts.map +1 -0
- package/dist/adapters/claude.js +90 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/gemini.d.ts +42 -0
- package/dist/adapters/gemini.d.ts.map +1 -0
- package/dist/adapters/gemini.js +133 -0
- package/dist/adapters/gemini.js.map +1 -0
- package/dist/adapters/index.d.ts +16 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +15 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/mock.d.ts +23 -0
- package/dist/adapters/mock.d.ts.map +1 -0
- package/dist/adapters/mock.js +107 -0
- package/dist/adapters/mock.js.map +1 -0
- package/dist/adapters/openai.d.ts +38 -0
- package/dist/adapters/openai.d.ts.map +1 -0
- package/dist/adapters/openai.js +105 -0
- package/dist/adapters/openai.js.map +1 -0
- package/dist/adapters/types.d.ts +65 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +26 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/config/load.d.ts +15 -0
- package/dist/config/load.d.ts.map +1 -0
- package/dist/config/load.js +46 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/schema.d.ts +260 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +58 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/context/manifest.d.ts +58 -0
- package/dist/context/manifest.d.ts.map +1 -0
- package/dist/context/manifest.js +49 -0
- package/dist/context/manifest.js.map +1 -0
- package/dist/context/redact.d.ts +26 -0
- package/dist/context/redact.d.ts.map +1 -0
- package/dist/context/redact.js +67 -0
- package/dist/context/redact.js.map +1 -0
- package/dist/git/status.d.ts +48 -0
- package/dist/git/status.d.ts.map +1 -0
- package/dist/git/status.js +79 -0
- package/dist/git/status.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/review/budget.d.ts +46 -0
- package/dist/review/budget.d.ts.map +1 -0
- package/dist/review/budget.js +83 -0
- package/dist/review/budget.js.map +1 -0
- package/dist/review/diff.d.ts +21 -0
- package/dist/review/diff.d.ts.map +1 -0
- package/dist/review/diff.js +55 -0
- package/dist/review/diff.js.map +1 -0
- package/dist/review/events.d.ts +76 -0
- package/dist/review/events.d.ts.map +1 -0
- package/dist/review/events.js +13 -0
- package/dist/review/events.js.map +1 -0
- package/dist/review/memory.d.ts +23 -0
- package/dist/review/memory.d.ts.map +1 -0
- package/dist/review/memory.js +42 -0
- package/dist/review/memory.js.map +1 -0
- package/dist/review/prompts.d.ts +34 -0
- package/dist/review/prompts.d.ts.map +1 -0
- package/dist/review/prompts.js +174 -0
- package/dist/review/prompts.js.map +1 -0
- package/dist/review/run-diff-review.d.ts +52 -0
- package/dist/review/run-diff-review.d.ts.map +1 -0
- package/dist/review/run-diff-review.js +258 -0
- package/dist/review/run-diff-review.js.map +1 -0
- package/dist/review/run-worker-reviewer.d.ts +72 -0
- package/dist/review/run-worker-reviewer.d.ts.map +1 -0
- package/dist/review/run-worker-reviewer.js +407 -0
- package/dist/review/run-worker-reviewer.js.map +1 -0
- package/dist/review/write.d.ts +44 -0
- package/dist/review/write.d.ts.map +1 -0
- package/dist/review/write.js +152 -0
- package/dist/review/write.js.map +1 -0
- package/dist/runs/decide.d.ts +45 -0
- package/dist/runs/decide.d.ts.map +1 -0
- package/dist/runs/decide.js +93 -0
- package/dist/runs/decide.js.map +1 -0
- package/dist/runs/folder.d.ts +42 -0
- package/dist/runs/folder.d.ts.map +1 -0
- package/dist/runs/folder.js +82 -0
- package/dist/runs/folder.js.map +1 -0
- package/dist/runs/list.d.ts +58 -0
- package/dist/runs/list.d.ts.map +1 -0
- package/dist/runs/list.js +117 -0
- package/dist/runs/list.js.map +1 -0
- package/dist/runs/types.d.ts +96 -0
- package/dist/runs/types.d.ts.map +1 -0
- package/dist/runs/types.js +13 -0
- package/dist/runs/types.js.map +1 -0
- package/dist/templates/install.d.ts +49 -0
- package/dist/templates/install.d.ts.map +1 -0
- package/dist/templates/install.js +154 -0
- package/dist/templates/install.js.map +1 -0
- package/package.json +34 -0
- package/src/adapters/claude.ts +133 -0
- package/src/adapters/gemini.ts +183 -0
- package/src/adapters/index.ts +21 -0
- package/src/adapters/mock.ts +125 -0
- package/src/adapters/openai.ts +150 -0
- package/src/adapters/types.ts +73 -0
- package/src/config/load.ts +61 -0
- package/src/config/schema.ts +64 -0
- package/src/context/manifest.ts +94 -0
- package/src/context/redact.ts +93 -0
- package/src/git/status.ts +108 -0
- package/src/index.ts +127 -0
- package/src/review/budget.ts +116 -0
- package/src/review/diff.ts +85 -0
- package/src/review/events.ts +86 -0
- package/src/review/memory.ts +57 -0
- package/src/review/prompts.ts +208 -0
- package/src/review/run-diff-review.ts +353 -0
- package/src/review/run-worker-reviewer.ts +528 -0
- package/src/review/write.ts +208 -0
- package/src/runs/decide.ts +153 -0
- package/src/runs/folder.ts +137 -0
- package/src/runs/list.ts +152 -0
- package/src/runs/types.ts +98 -0
- package/src/templates/install.ts +198 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for run metadata persisted in .almightygpt/runs/<id>/run.json.
|
|
3
|
+
*
|
|
4
|
+
* The run folder is the machine-readable side of the dual artifact strategy.
|
|
5
|
+
* It is gitignored by default — git holds the human review files only.
|
|
6
|
+
*
|
|
7
|
+
* run.json must contain enough information to:
|
|
8
|
+
* - reproduce or re-render the run,
|
|
9
|
+
* - audit what was sent to which provider,
|
|
10
|
+
* - drive the future VS Code / dashboard / CLI history views.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export type RunType =
|
|
14
|
+
| "review-diff"
|
|
15
|
+
| "review-path"
|
|
16
|
+
| "review-requirement"
|
|
17
|
+
| "review-worker-reviewer";
|
|
18
|
+
|
|
19
|
+
export type RunStatus = "running" | "completed" | "failed" | "aborted_budget";
|
|
20
|
+
|
|
21
|
+
export interface AgentMetrics {
|
|
22
|
+
agent: string;
|
|
23
|
+
role: "worker" | "reviewer" | "judge";
|
|
24
|
+
provider: string;
|
|
25
|
+
model: string;
|
|
26
|
+
tokensIn: number;
|
|
27
|
+
tokensOut: number;
|
|
28
|
+
costUsd: number;
|
|
29
|
+
latencyMs: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface RunMetadata {
|
|
33
|
+
/** Folder name: <YYYY-MM-DD-HHmmss>-<topic>-<random>. */
|
|
34
|
+
id: string;
|
|
35
|
+
/** Kind of run. */
|
|
36
|
+
type: RunType;
|
|
37
|
+
/** ISO timestamp when the run started. */
|
|
38
|
+
createdAt: string;
|
|
39
|
+
/** ISO timestamp when the run completed (or failed). */
|
|
40
|
+
finishedAt?: string;
|
|
41
|
+
/** Repo root absolute path. */
|
|
42
|
+
workspacePath: string;
|
|
43
|
+
/** Topic slug. */
|
|
44
|
+
topic: string;
|
|
45
|
+
/** Optional git context. */
|
|
46
|
+
git?: {
|
|
47
|
+
branch?: string;
|
|
48
|
+
commit?: string;
|
|
49
|
+
dirty: boolean;
|
|
50
|
+
};
|
|
51
|
+
/** What kicked off the run. */
|
|
52
|
+
input: {
|
|
53
|
+
source: "diff" | "diff-range" | "path" | "requirement-file" | "ask";
|
|
54
|
+
range?: string;
|
|
55
|
+
path?: string;
|
|
56
|
+
};
|
|
57
|
+
/** Agents that participated, with config snapshot. */
|
|
58
|
+
agents: { name: string; role: string; provider: string; enabled: boolean }[];
|
|
59
|
+
/** Adapter package versions. */
|
|
60
|
+
adapterVersions: { adapter: string; version: string }[];
|
|
61
|
+
/** Final status. */
|
|
62
|
+
status: RunStatus;
|
|
63
|
+
/** Per-agent metrics. */
|
|
64
|
+
metrics: AgentMetrics[];
|
|
65
|
+
/** Aggregate totals. */
|
|
66
|
+
totals: {
|
|
67
|
+
tokensIn: number;
|
|
68
|
+
tokensOut: number;
|
|
69
|
+
costUsd: number;
|
|
70
|
+
latencyMs: number;
|
|
71
|
+
};
|
|
72
|
+
/** Where the human-facing review file lives (relative to workspacePath). */
|
|
73
|
+
reviewPath?: string;
|
|
74
|
+
/** Where the context manifest lives (relative to the run folder). */
|
|
75
|
+
contextManifestPath?: string;
|
|
76
|
+
/** Optional error info if status is failed. */
|
|
77
|
+
error?: { name: string; message: string };
|
|
78
|
+
/** Budget caps that were in effect. */
|
|
79
|
+
budget: {
|
|
80
|
+
maxCostPerRunUsd: number;
|
|
81
|
+
maxTokensPerRun: number;
|
|
82
|
+
};
|
|
83
|
+
/** Human decision recorded via `almightygpt decide`. */
|
|
84
|
+
decision?: HumanDecision;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type DecisionStatus =
|
|
88
|
+
| "approved"
|
|
89
|
+
| "needs_changes"
|
|
90
|
+
| "rejected"
|
|
91
|
+
| "deferred";
|
|
92
|
+
|
|
93
|
+
export interface HumanDecision {
|
|
94
|
+
status: DecisionStatus;
|
|
95
|
+
note?: string;
|
|
96
|
+
decidedBy?: string;
|
|
97
|
+
decidedAt: string;
|
|
98
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convention Pack template installer.
|
|
3
|
+
*
|
|
4
|
+
* Given a target repo and a stack name, copy the template files from
|
|
5
|
+
* @almightygpt/templates into the target. Each file is checked against
|
|
6
|
+
* git status before being written; existing files are skipped unless
|
|
7
|
+
* `force` is set.
|
|
8
|
+
*
|
|
9
|
+
* Behavior intentionally narrow for MVP 1:
|
|
10
|
+
* - One stack supported: "node-ts".
|
|
11
|
+
* - No prompts, no diffing — caller decides force vs skip.
|
|
12
|
+
* - Non-existent target files are written unconditionally.
|
|
13
|
+
* - Existing tracked files are protected by `assertSafeToWrite`.
|
|
14
|
+
* - Existing untracked files are surfaced as dirty too (intentional).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { mkdir, readdir, readFile, rename, stat, writeFile } from "node:fs/promises";
|
|
18
|
+
import { dirname, join, relative } from "node:path";
|
|
19
|
+
import { existsSync } from "node:fs";
|
|
20
|
+
import { fileURLToPath } from "node:url";
|
|
21
|
+
import { assertSafeToWrite } from "../git/status.js";
|
|
22
|
+
|
|
23
|
+
export interface InstallOptions {
|
|
24
|
+
/** Target repo root where the convention should be installed. */
|
|
25
|
+
targetDir: string;
|
|
26
|
+
/** Stack template name. MVP 1 only supports "node-ts". */
|
|
27
|
+
stack: string;
|
|
28
|
+
/** Overwrite without git-status safety check. Default false. */
|
|
29
|
+
force?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* For each existing file, rename it to `<file>.almighty-bak` and write the
|
|
32
|
+
* new template. Safer than --force when upgrading an existing install.
|
|
33
|
+
* If a `.almighty-bak` already exists, the second backup is suffixed with
|
|
34
|
+
* a counter (e.g. `.almighty-bak.2`). Default false.
|
|
35
|
+
*/
|
|
36
|
+
backup?: boolean;
|
|
37
|
+
/** Plan only — log what would happen, write nothing. Default false. */
|
|
38
|
+
dryRun?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface InstallResult {
|
|
42
|
+
written: string[];
|
|
43
|
+
skipped: { path: string; reason: string }[];
|
|
44
|
+
/** Files that were backed up before being overwritten. */
|
|
45
|
+
backedUp: { from: string; to: string }[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
49
|
+
// In dev (ts-node-esm) and in built form, the templates package lives at the
|
|
50
|
+
// same monorepo root. Find it by walking up until we hit packages/templates.
|
|
51
|
+
function findTemplatesRoot(stack: string): string {
|
|
52
|
+
// From packages/core/dist/templates/install.js → up to monorepo root.
|
|
53
|
+
let cur = dirname(__filename);
|
|
54
|
+
for (let i = 0; i < 8; i++) {
|
|
55
|
+
const candidate = join(cur, "..", "templates");
|
|
56
|
+
if (existsSync(join(candidate, "package.json"))) {
|
|
57
|
+
const distRoot = join(candidate, "dist", stack);
|
|
58
|
+
const srcRoot = join(candidate, "src", stack);
|
|
59
|
+
if (existsSync(distRoot)) return distRoot;
|
|
60
|
+
if (existsSync(srcRoot)) return srcRoot;
|
|
61
|
+
}
|
|
62
|
+
cur = join(cur, "..");
|
|
63
|
+
}
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Could not locate @almightygpt/templates for stack "${stack}". ` +
|
|
66
|
+
`Searched relative to ${__filename}. Did you forget to run \`npm run build\`?`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Recursively walk a directory and return relative paths of every file.
|
|
72
|
+
*/
|
|
73
|
+
async function walk(root: string, prefix = ""): Promise<string[]> {
|
|
74
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
75
|
+
const out: string[] = [];
|
|
76
|
+
for (const entry of entries) {
|
|
77
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
78
|
+
const abs = join(root, entry.name);
|
|
79
|
+
if (entry.isDirectory()) {
|
|
80
|
+
out.push(...(await walk(abs, rel)));
|
|
81
|
+
} else if (entry.isFile()) {
|
|
82
|
+
out.push(rel);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function installTemplate(
|
|
89
|
+
options: InstallOptions,
|
|
90
|
+
): Promise<InstallResult> {
|
|
91
|
+
const {
|
|
92
|
+
targetDir,
|
|
93
|
+
stack,
|
|
94
|
+
force = false,
|
|
95
|
+
backup = false,
|
|
96
|
+
dryRun = false,
|
|
97
|
+
} = options;
|
|
98
|
+
|
|
99
|
+
const sourceRoot = findTemplatesRoot(stack);
|
|
100
|
+
const files = await walk(sourceRoot);
|
|
101
|
+
|
|
102
|
+
const written: string[] = [];
|
|
103
|
+
const skipped: { path: string; reason: string }[] = [];
|
|
104
|
+
const backedUp: { from: string; to: string }[] = [];
|
|
105
|
+
|
|
106
|
+
for (const rel of files) {
|
|
107
|
+
// Some template paths use a sentinel "almightygpt" folder because dotfile
|
|
108
|
+
// folders can be awkward to author. Translate it to ".almightygpt" at
|
|
109
|
+
// install time.
|
|
110
|
+
const destRel = rel.replace(/(^|\/)almightygpt(\/|$)/, "$1.almightygpt$2");
|
|
111
|
+
const srcAbs = join(sourceRoot, rel);
|
|
112
|
+
const destAbs = join(targetDir, destRel);
|
|
113
|
+
|
|
114
|
+
// Decision tree for existing destination files:
|
|
115
|
+
// --backup: rename existing → <file>.almighty-bak, then write new
|
|
116
|
+
// --force: overwrite (still respects dirty check unless force=true)
|
|
117
|
+
// default: skip with reason "already exists" — safest behavior
|
|
118
|
+
if (existsSync(destAbs)) {
|
|
119
|
+
if (backup) {
|
|
120
|
+
// Backup mode: even if the file is dirty, preserve it via rename.
|
|
121
|
+
// No git-status check needed because we're not destroying anything.
|
|
122
|
+
if (dryRun) {
|
|
123
|
+
written.push(destRel);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const backupRel = await pickBackupPath(destRel, targetDir);
|
|
127
|
+
const backupAbs = join(targetDir, backupRel);
|
|
128
|
+
await rename(destAbs, backupAbs);
|
|
129
|
+
backedUp.push({ from: destRel, to: backupRel });
|
|
130
|
+
// Fall through to write the new version below.
|
|
131
|
+
} else {
|
|
132
|
+
try {
|
|
133
|
+
await assertSafeToWrite(targetDir, destRel, force);
|
|
134
|
+
// Tracked and clean (or --force without --backup): skip by default.
|
|
135
|
+
// --force is required to actually overwrite a clean file.
|
|
136
|
+
if (!force) {
|
|
137
|
+
skipped.push({
|
|
138
|
+
path: destRel,
|
|
139
|
+
reason: "already exists (use --backup or --force to update)",
|
|
140
|
+
});
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
} catch (err) {
|
|
144
|
+
skipped.push({
|
|
145
|
+
path: destRel,
|
|
146
|
+
reason:
|
|
147
|
+
err instanceof Error ? err.message.split("\n")[0]! : String(err),
|
|
148
|
+
});
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (dryRun) {
|
|
155
|
+
written.push(destRel);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const content = await readFile(srcAbs);
|
|
160
|
+
await mkdir(dirname(destAbs), { recursive: true });
|
|
161
|
+
await writeFile(destAbs, content);
|
|
162
|
+
written.push(destRel);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return { written, skipped, backedUp };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Find a free `<file>.almighty-bak[.N]` slot for an existing file.
|
|
170
|
+
* Avoids overwriting a previous backup if the user runs --backup twice.
|
|
171
|
+
*/
|
|
172
|
+
async function pickBackupPath(
|
|
173
|
+
destRel: string,
|
|
174
|
+
targetDir: string,
|
|
175
|
+
): Promise<string> {
|
|
176
|
+
const base = `${destRel}.almighty-bak`;
|
|
177
|
+
if (!existsSync(join(targetDir, base))) return base;
|
|
178
|
+
for (let i = 2; i < 1000; i++) {
|
|
179
|
+
const candidate = `${base}.${i}`;
|
|
180
|
+
if (!existsSync(join(targetDir, candidate))) return candidate;
|
|
181
|
+
}
|
|
182
|
+
throw new Error(`Could not find a free backup slot for ${destRel}.`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function relativeFromCwd(targetDir: string, absPath: string): string {
|
|
186
|
+
const rel = relative(targetDir, absPath);
|
|
187
|
+
return rel.startsWith("..") ? absPath : rel;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Stat helper used by callers that want to detect an existing install. */
|
|
191
|
+
export async function hasExistingConfig(targetDir: string): Promise<boolean> {
|
|
192
|
+
try {
|
|
193
|
+
await stat(join(targetDir, ".almightygpt", "config.yaml"));
|
|
194
|
+
return true;
|
|
195
|
+
} catch {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}
|