nomoreide 0.1.62 → 0.1.64

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.
@@ -0,0 +1,32 @@
1
+ import { type ConfigFileFormat } from "./config-files.js";
2
+ /**
3
+ * A config file (`.env`, `appsettings.json`, `application.yml`) that was edited
4
+ * after the service process started — so the live process is running with the
5
+ * old values baked in.
6
+ */
7
+ export interface StaleConfigFile {
8
+ relativePath: string;
9
+ path: string;
10
+ format: ConfigFileFormat;
11
+ modifiedAt: string;
12
+ }
13
+ export interface RuntimeEnvStatus {
14
+ running: boolean;
15
+ startedAt?: string;
16
+ /** Config files modified after `startedAt` (most-recent first). */
17
+ staleFiles: StaleConfigFile[];
18
+ /** True when a running process is using an out-of-date configuration. */
19
+ stale: boolean;
20
+ }
21
+ /**
22
+ * A running process bakes in its environment at exec time — the OS gives no way
23
+ * to mutate it in place, so a `.env` edit only takes effect on the next launch.
24
+ * This compares each detected config file's mtime against when the service
25
+ * started so the UI can flag "running stale" and offer a one-click reload,
26
+ * instead of leaving the user to restart-and-pray.
27
+ */
28
+ export declare function computeRuntimeEnvStatus(input: {
29
+ cwd: string;
30
+ running: boolean;
31
+ startedAt?: string;
32
+ }): Promise<RuntimeEnvStatus>;
@@ -0,0 +1,42 @@
1
+ import { stat } from "node:fs/promises";
2
+ import { detectConfigFiles } from "./config-files.js";
3
+ /**
4
+ * A running process bakes in its environment at exec time — the OS gives no way
5
+ * to mutate it in place, so a `.env` edit only takes effect on the next launch.
6
+ * This compares each detected config file's mtime against when the service
7
+ * started so the UI can flag "running stale" and offer a one-click reload,
8
+ * instead of leaving the user to restart-and-pray.
9
+ */
10
+ export async function computeRuntimeEnvStatus(input) {
11
+ const { cwd, running, startedAt } = input;
12
+ if (!running || !startedAt) {
13
+ return { running, startedAt, staleFiles: [], stale: false };
14
+ }
15
+ const startedMs = Date.parse(startedAt);
16
+ if (Number.isNaN(startedMs)) {
17
+ return { running, startedAt, staleFiles: [], stale: false };
18
+ }
19
+ const files = await detectConfigFiles(cwd);
20
+ const staleFiles = [];
21
+ for (const file of files) {
22
+ let mtimeMs;
23
+ try {
24
+ mtimeMs = (await stat(file.path)).mtimeMs;
25
+ }
26
+ catch {
27
+ continue; // vanished between detection and stat — ignore
28
+ }
29
+ // 1s skew guard so a save that races the spawn isn't a false positive.
30
+ if (mtimeMs > startedMs + 1000) {
31
+ staleFiles.push({
32
+ relativePath: file.relativePath,
33
+ path: file.path,
34
+ format: file.format,
35
+ modifiedAt: new Date(mtimeMs).toISOString(),
36
+ });
37
+ }
38
+ }
39
+ staleFiles.sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt));
40
+ return { running, startedAt, staleFiles, stale: staleFiles.length > 0 };
41
+ }
42
+ //# sourceMappingURL=env-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-runtime.js","sourceRoot":"","sources":["../../src/core/env-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAyB,MAAM,mBAAmB,CAAC;AAuB7E;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,KAI7C;IACC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAC1C,IAAI,CAAC,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC;QAC3B,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC9D,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC9D,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAsB,EAAE,CAAC;IACzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,CAAC,+CAA+C;QAC3D,CAAC;QACD,uEAAuE;QACvE,IAAI,OAAO,GAAG,SAAS,GAAG,IAAI,EAAE,CAAC;YAC/B,UAAU,CAAC,IAAI,CAAC;gBACd,YAAY,EAAE,IAAI,CAAC,YAAY;gBAC/B,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,UAAU,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE;aAC5C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IACpE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,55 @@
1
+ import type { AgentSessionStore } from "./agent-sessions.js";
2
+ import type { ReproBundleBuilder } from "./repro-bundle.js";
3
+ import { SnapshotManager } from "./snapshot-manager.js";
4
+ /**
5
+ * The Error → Fix loop. Closes the AI-native circle: turn an Error Inbox
6
+ * incident into one agent task whose result is a reviewable, one-click-revertable
7
+ * change-set.
8
+ *
9
+ * Today the separate features (Error Inbox, repro bundle, snapshot/restore,
10
+ * change-set review) are chained by hand. {@link FixLoop.prepare} stitches them:
11
+ * it builds the repro bundle, snapshots the working tree, and records an agent
12
+ * session pinned to that snapshot — so whatever the in-dock agent then edits
13
+ * surfaces in Agent → Changes with a "restore to before this session" button.
14
+ *
15
+ * Why the snapshot is taken here rather than auto-firing: the in-dock agent
16
+ * runs the Claude/Codex CLI directly (it does not go through NoMoreIDE's MCP),
17
+ * so the MCP recording wrapper's auto-snapshot never triggers for a dock run.
18
+ * The fix endpoint must capture the pre-fix state itself.
19
+ */
20
+ export interface FixPreparation {
21
+ /** Recorded agent session id; the change-set/restore endpoints key off this. */
22
+ sessionId: string;
23
+ /** The fix prompt (repro bundle wrapped in a fix instruction) for the dock. */
24
+ prompt: string;
25
+ /** Repository the snapshot was taken in (where the agent will edit). */
26
+ repoPath: string;
27
+ /** Snapshot sha, or undefined when the repo isn't a git working tree. */
28
+ snapshotSha?: string;
29
+ }
30
+ export interface FixLoopOptions {
31
+ reproBundle: ReproBundleBuilder;
32
+ agentSessions: AgentSessionStore;
33
+ /** Resolve the repo the agent will edit (the selected git repository). */
34
+ resolveRepoPath: () => Promise<string>;
35
+ /** Injectable for tests. */
36
+ snapshotManagerFor?: (repoPath: string) => Pick<SnapshotManager, "snapshot" | "prune">;
37
+ now?: () => number;
38
+ }
39
+ export declare class FixLoop {
40
+ private readonly reproBundle;
41
+ private readonly agentSessions;
42
+ private readonly resolveRepoPath;
43
+ private readonly snapshotManagerFor;
44
+ private readonly now;
45
+ constructor(options: FixLoopOptions);
46
+ /**
47
+ * Prepare a fix for an incident: build the repro bundle, snapshot the working
48
+ * tree, and record the session. Returns null when the incident is unknown.
49
+ * Never throws on a missing/non-git repo — the session is recorded without a
50
+ * snapshot so the run still proceeds (just no reviewable change-set).
51
+ */
52
+ prepare(incidentId: number): Promise<FixPreparation | null>;
53
+ }
54
+ /** Wrap the repro-bundle markdown in a concise fix instruction for the agent. */
55
+ export declare function buildFixPrompt(markdown: string): string;
@@ -0,0 +1,73 @@
1
+ import { SnapshotManager } from "./snapshot-manager.js";
2
+ const SNAPSHOT_KEEP = 50;
3
+ export class FixLoop {
4
+ reproBundle;
5
+ agentSessions;
6
+ resolveRepoPath;
7
+ snapshotManagerFor;
8
+ now;
9
+ constructor(options) {
10
+ this.reproBundle = options.reproBundle;
11
+ this.agentSessions = options.agentSessions;
12
+ this.resolveRepoPath = options.resolveRepoPath;
13
+ this.snapshotManagerFor =
14
+ options.snapshotManagerFor ?? ((repoPath) => new SnapshotManager(repoPath));
15
+ this.now = options.now ?? Date.now;
16
+ }
17
+ /**
18
+ * Prepare a fix for an incident: build the repro bundle, snapshot the working
19
+ * tree, and record the session. Returns null when the incident is unknown.
20
+ * Never throws on a missing/non-git repo — the session is recorded without a
21
+ * snapshot so the run still proceeds (just no reviewable change-set).
22
+ */
23
+ async prepare(incidentId) {
24
+ const bundle = await this.reproBundle.build(incidentId);
25
+ if (!bundle)
26
+ return null;
27
+ const repoPath = await this.resolveRepoPath();
28
+ const startedAt = new Date(this.now()).toISOString();
29
+ const session = {
30
+ id: `s-${this.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
31
+ repoPath,
32
+ startedAt,
33
+ lastToolAt: startedAt,
34
+ toolCount: 0,
35
+ };
36
+ try {
37
+ const manager = this.snapshotManagerFor(repoPath);
38
+ const snapshot = await manager.snapshot(`fix incident ${incidentId}`);
39
+ session.snapshotSha = snapshot.sha;
40
+ session.snapshotRef = snapshot.ref;
41
+ await manager.prune(SNAPSHOT_KEEP);
42
+ }
43
+ catch {
44
+ // Not a git repo (or git unavailable) — record the session without a
45
+ // snapshot. The fix still runs; there just isn't a change-set to review.
46
+ }
47
+ try {
48
+ await this.agentSessions.save(session);
49
+ }
50
+ catch {
51
+ // Persistence is best-effort; never block the fix on it.
52
+ }
53
+ return {
54
+ sessionId: session.id,
55
+ prompt: buildFixPrompt(bundle.markdown),
56
+ repoPath,
57
+ snapshotSha: session.snapshotSha,
58
+ };
59
+ }
60
+ }
61
+ /** Wrap the repro-bundle markdown in a concise fix instruction for the agent. */
62
+ export function buildFixPrompt(markdown) {
63
+ return [
64
+ "A bug was detected in this workspace. Below is an automatically assembled repro bundle: the error, the affected file's diff, recent logs, the service's runtime state, and its environment with secrets masked.",
65
+ "",
66
+ "Investigate the root cause and apply the smallest change that resolves it. When you are done, briefly summarize what you changed and why. Your edits are snapshotted, so they can be reviewed and reverted as a single change-set.",
67
+ "",
68
+ "---",
69
+ "",
70
+ markdown,
71
+ ].join("\n");
72
+ }
73
+ //# sourceMappingURL=fix-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fix-loop.js","sourceRoot":"","sources":["../../src/core/fix-loop.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAiB,MAAM,uBAAuB,CAAC;AAwCvE,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB,MAAM,OAAO,OAAO;IACD,WAAW,CAAqB;IAChC,aAAa,CAAoB;IACjC,eAAe,CAAwB;IACvC,kBAAkB,CAEc;IAChC,GAAG,CAAe;IAEnC,YAAY,OAAuB;QACjC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;QAC3C,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;QAC/C,IAAI,CAAC,kBAAkB;YACrB,OAAO,CAAC,kBAAkB,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC9E,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACrC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,OAAO,CAAC,UAAkB;QAC9B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAEzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;QAC9C,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACrD,MAAM,OAAO,GAAiB;YAC5B,EAAE,EAAE,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;YAC5E,QAAQ;YACR,SAAS;YACT,UAAU,EAAE,SAAS;YACrB,SAAS,EAAE,CAAC;SACb,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;YAClD,MAAM,QAAQ,GAAa,MAAM,OAAO,CAAC,QAAQ,CAAC,gBAAgB,UAAU,EAAE,CAAC,CAAC;YAChF,OAAO,CAAC,WAAW,GAAG,QAAQ,CAAC,GAAG,CAAC;YACnC,OAAO,CAAC,WAAW,GAAG,QAAQ,CAAC,GAAG,CAAC;YACnC,MAAM,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,qEAAqE;YACrE,yEAAyE;QAC3E,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,yDAAyD;QAC3D,CAAC;QAED,OAAO;YACL,SAAS,EAAE,OAAO,CAAC,EAAE;YACrB,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC;YACvC,QAAQ;YACR,WAAW,EAAE,OAAO,CAAC,WAAW;SACjC,CAAC;IACJ,CAAC;CACF;AAED,iFAAiF;AACjF,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,OAAO;QACL,iNAAiN;QACjN,EAAE;QACF,oOAAoO;QACpO,EAAE;QACF,KAAK;QACL,EAAE;QACF,QAAQ;KACT,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}