@matteeh/osq 0.1.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/LICENSE +21 -0
- package/README.md +183 -0
- package/dist/cli/approve.d.ts +6 -0
- package/dist/cli/approve.d.ts.map +1 -0
- package/dist/cli/approve.js +26 -0
- package/dist/cli/approve.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +5 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/index.d.ts +4 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +76 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +4 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +19 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/new.d.ts +4 -0
- package/dist/cli/new.d.ts.map +1 -0
- package/dist/cli/new.js +15 -0
- package/dist/cli/new.js.map +1 -0
- package/dist/cli/report.d.ts +10 -0
- package/dist/cli/report.d.ts.map +1 -0
- package/dist/cli/report.js +84 -0
- package/dist/cli/report.js.map +1 -0
- package/dist/cli/setup.d.ts +2 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +10 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli/show.d.ts +7 -0
- package/dist/cli/show.d.ts.map +1 -0
- package/dist/cli/show.js +27 -0
- package/dist/cli/show.js.map +1 -0
- package/dist/cli/status.d.ts +7 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +23 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/watch.d.ts +9 -0
- package/dist/cli/watch.d.ts.map +1 -0
- package/dist/cli/watch.js +20 -0
- package/dist/cli/watch.js.map +1 -0
- package/dist/core/approve.d.ts +11 -0
- package/dist/core/approve.d.ts.map +1 -0
- package/dist/core/approve.js +52 -0
- package/dist/core/approve.js.map +1 -0
- package/dist/core/config.d.ts +53 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +124 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/hasher.d.ts +4 -0
- package/dist/core/hasher.d.ts.map +1 -0
- package/dist/core/hasher.js +49 -0
- package/dist/core/hasher.js.map +1 -0
- package/dist/core/init.d.ts +12 -0
- package/dist/core/init.d.ts.map +1 -0
- package/dist/core/init.js +151 -0
- package/dist/core/init.js.map +1 -0
- package/dist/core/linter.d.ts +8 -0
- package/dist/core/linter.d.ts.map +1 -0
- package/dist/core/linter.js +100 -0
- package/dist/core/linter.js.map +1 -0
- package/dist/core/lock.d.ts +17 -0
- package/dist/core/lock.d.ts.map +1 -0
- package/dist/core/lock.js +104 -0
- package/dist/core/lock.js.map +1 -0
- package/dist/core/logger.d.ts +32 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +106 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/new.d.ts +11 -0
- package/dist/core/new.d.ts.map +1 -0
- package/dist/core/new.js +79 -0
- package/dist/core/new.js.map +1 -0
- package/dist/core/parser.d.ts +32 -0
- package/dist/core/parser.d.ts.map +1 -0
- package/dist/core/parser.js +87 -0
- package/dist/core/parser.js.map +1 -0
- package/dist/core/report.d.ts +54 -0
- package/dist/core/report.d.ts.map +1 -0
- package/dist/core/report.js +475 -0
- package/dist/core/report.js.map +1 -0
- package/dist/core/show.d.ts +50 -0
- package/dist/core/show.d.ts.map +1 -0
- package/dist/core/show.js +374 -0
- package/dist/core/show.js.map +1 -0
- package/dist/core/state.d.ts +25 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/state.js +180 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/status.d.ts +10 -0
- package/dist/core/status.d.ts.map +1 -0
- package/dist/core/status.js +106 -0
- package/dist/core/status.js.map +1 -0
- package/dist/harness/agy.d.ts +31 -0
- package/dist/harness/agy.d.ts.map +1 -0
- package/dist/harness/agy.js +251 -0
- package/dist/harness/agy.js.map +1 -0
- package/dist/harness/index.d.ts +7 -0
- package/dist/harness/index.d.ts.map +1 -0
- package/dist/harness/index.js +20 -0
- package/dist/harness/index.js.map +1 -0
- package/dist/harness/mock.d.ts +19 -0
- package/dist/harness/mock.d.ts.map +1 -0
- package/dist/harness/mock.js +62 -0
- package/dist/harness/mock.js.map +1 -0
- package/dist/harness/opencode.d.ts +39 -0
- package/dist/harness/opencode.d.ts.map +1 -0
- package/dist/harness/opencode.js +394 -0
- package/dist/harness/opencode.js.map +1 -0
- package/dist/harness/process.d.ts +26 -0
- package/dist/harness/process.d.ts.map +1 -0
- package/dist/harness/process.js +99 -0
- package/dist/harness/process.js.map +1 -0
- package/dist/harness/stream.d.ts +34 -0
- package/dist/harness/stream.d.ts.map +1 -0
- package/dist/harness/stream.js +75 -0
- package/dist/harness/stream.js.map +1 -0
- package/dist/harness/types.d.ts +53 -0
- package/dist/harness/types.d.ts.map +1 -0
- package/dist/harness/types.js +10 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/watcher/archiver.d.ts +5 -0
- package/dist/watcher/archiver.d.ts.map +1 -0
- package/dist/watcher/archiver.js +66 -0
- package/dist/watcher/archiver.js.map +1 -0
- package/dist/watcher/loop.d.ts +34 -0
- package/dist/watcher/loop.d.ts.map +1 -0
- package/dist/watcher/loop.js +235 -0
- package/dist/watcher/loop.js.map +1 -0
- package/dist/watcher/runner.d.ts +70 -0
- package/dist/watcher/runner.d.ts.map +1 -0
- package/dist/watcher/runner.js +604 -0
- package/dist/watcher/runner.js.map +1 -0
- package/package.json +66 -0
- package/templates/.env.example +3 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared harness stream helpers. Harness adapters translate an unbounded stdout
|
|
3
|
+
* stream of JSON lines into `events.jsonl`; the record coercion, string picking,
|
|
4
|
+
* timestamp resolution, and line buffering are identical across adapters and
|
|
5
|
+
* live here so each adapter only owns its event translation.
|
|
6
|
+
*/
|
|
7
|
+
/** Coerce an unknown JSON value into a plain object, or `undefined` for arrays and primitives. */
|
|
8
|
+
export function asRecord(value) {
|
|
9
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
10
|
+
? value
|
|
11
|
+
: undefined;
|
|
12
|
+
}
|
|
13
|
+
/** Return the first candidate that is a non-empty string, skipping every other type. */
|
|
14
|
+
export function firstNonEmptyString(...values) {
|
|
15
|
+
for (const value of values) {
|
|
16
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Resolve an event timestamp to ISO form. Harnesses report either a top-level
|
|
24
|
+
* `timestamp` or, for Antigravity step updates, a nested `step_update.timestamp`.
|
|
25
|
+
* Invalid or missing timestamps fall back to the current time so an event is
|
|
26
|
+
* never written without one.
|
|
27
|
+
*/
|
|
28
|
+
export function resolveEventTimestamp(eventObj) {
|
|
29
|
+
const stepUpdate = asRecord(eventObj.step_update);
|
|
30
|
+
const candidate = eventObj.timestamp ?? stepUpdate?.timestamp;
|
|
31
|
+
if (typeof candidate === 'number' || typeof candidate === 'string') {
|
|
32
|
+
const date = new Date(candidate);
|
|
33
|
+
if (!Number.isNaN(date.getTime())) {
|
|
34
|
+
return date.toISOString();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return new Date().toISOString();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Buffers arbitrarily chunked stdout and delivers complete, non-blank lines to
|
|
41
|
+
* an async handler one at a time in arrival order. A handler rejection is
|
|
42
|
+
* swallowed so a single malformed line can never abort the rest of the stream;
|
|
43
|
+
* `flush` drains whatever partial line remains and resolves after every queued
|
|
44
|
+
* handler has settled.
|
|
45
|
+
*/
|
|
46
|
+
export class EventStreamParser {
|
|
47
|
+
onLine;
|
|
48
|
+
buffer = '';
|
|
49
|
+
pending = Promise.resolve();
|
|
50
|
+
constructor(onLine) {
|
|
51
|
+
this.onLine = onLine;
|
|
52
|
+
}
|
|
53
|
+
feed(chunk) {
|
|
54
|
+
this.buffer += chunk;
|
|
55
|
+
const lines = this.buffer.split('\n');
|
|
56
|
+
this.buffer = lines.pop() ?? '';
|
|
57
|
+
for (const line of lines) {
|
|
58
|
+
this.enqueue(line);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async flush() {
|
|
62
|
+
if (this.buffer.trim()) {
|
|
63
|
+
this.enqueue(this.buffer);
|
|
64
|
+
}
|
|
65
|
+
this.buffer = '';
|
|
66
|
+
await this.pending;
|
|
67
|
+
}
|
|
68
|
+
enqueue(line) {
|
|
69
|
+
if (!line.trim()) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
this.pending = this.pending.then(() => this.onLine(line).catch(() => { }));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/harness/stream.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,kGAAkG;AAClG,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAChE,CAAC,CAAE,KAAiC;QACpC,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,mBAAmB,CAAC,GAAG,MAAiB;IACtD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAiC;IACrE,MAAM,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,QAAQ,CAAC,SAAS,IAAI,UAAU,EAAE,SAAS,CAAC;IAC9D,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QACnE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,iBAAiB;IAIC;IAHrB,MAAM,GAAG,EAAE,CAAC;IACZ,OAAO,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAEnD,YAA6B,MAAuC;QAAvC,WAAM,GAAN,MAAM,CAAiC;IAAG,CAAC;IAExE,IAAI,CAAC,KAAa;QAChB,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;QAEhC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;YACvB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,MAAM,IAAI,CAAC,OAAO,CAAC;IACrB,CAAC;IAEO,OAAO,CAAC,IAAY;QAC1B,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC;IAC5E,CAAC;CACF"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { OsqConfig } from '../core/config.js';
|
|
2
|
+
import type { Logger } from '../core/logger.js';
|
|
3
|
+
export type HarnessEventType = 'started' | 'tokens' | 'tool' | 'text' | 'file_changed' | 'verify_ran' | 'result_written' | 'exited' | 'done' | 'dead';
|
|
4
|
+
export interface ToolEventData {
|
|
5
|
+
tool: string;
|
|
6
|
+
summary: string;
|
|
7
|
+
}
|
|
8
|
+
export interface TextEventData {
|
|
9
|
+
readonly text: string;
|
|
10
|
+
}
|
|
11
|
+
export interface DoneEventData {
|
|
12
|
+
readonly task: string;
|
|
13
|
+
}
|
|
14
|
+
export interface DeadEventData {
|
|
15
|
+
readonly task: string;
|
|
16
|
+
readonly reason: string;
|
|
17
|
+
}
|
|
18
|
+
export interface HarnessEvent {
|
|
19
|
+
type: HarnessEventType;
|
|
20
|
+
timestamp: string;
|
|
21
|
+
data?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
export interface SpawnTaskOptions {
|
|
24
|
+
projectRoot: string;
|
|
25
|
+
specFolderPath: string;
|
|
26
|
+
taskNumber: string;
|
|
27
|
+
taskTitle: string;
|
|
28
|
+
verifyCommand: string;
|
|
29
|
+
scope: string[];
|
|
30
|
+
entry: string[];
|
|
31
|
+
skills: string[];
|
|
32
|
+
tier: 'coding' | 'smart';
|
|
33
|
+
timeoutSeconds?: number;
|
|
34
|
+
config?: OsqConfig;
|
|
35
|
+
logger?: Logger;
|
|
36
|
+
onSpawn?: (pid: number) => Promise<void> | void;
|
|
37
|
+
}
|
|
38
|
+
export interface SpawnResult {
|
|
39
|
+
exitCode: number;
|
|
40
|
+
error?: string;
|
|
41
|
+
timedOut?: boolean;
|
|
42
|
+
signal?: NodeJS.Signals | string | null;
|
|
43
|
+
pid?: number;
|
|
44
|
+
elapsedMs?: number;
|
|
45
|
+
}
|
|
46
|
+
export interface HarnessAdapter {
|
|
47
|
+
readonly name: string;
|
|
48
|
+
setup(projectRoot: string, config: OsqConfig): Promise<void>;
|
|
49
|
+
spawn(options: SpawnTaskOptions): Promise<SpawnResult>;
|
|
50
|
+
preflight?(projectRoot: string, config: OsqConfig): Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
export declare function appendHarnessEvent(specFolderPath: string, taskNumber: string, event: HarnessEvent): Promise<void>;
|
|
53
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/harness/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,QAAQ,GACR,MAAM,GACN,MAAM,GACN,cAAc,GACd,YAAY,GACZ,gBAAgB,GAChB,QAAQ,GACR,MAAM,GACN,MAAM,CAAC;AAEX,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACjD;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,KAAK,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACvD,SAAS,CAAC,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnE;AAED,wBAAsB,kBAAkB,CACtC,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,IAAI,CAAC,CAOf"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export async function appendHarnessEvent(specFolderPath, taskNumber, event) {
|
|
4
|
+
const eventsDir = path.join(specFolderPath, '.run', 'events');
|
|
5
|
+
await fs.mkdir(eventsDir, { recursive: true });
|
|
6
|
+
const eventFilePath = path.join(eventsDir, `${taskNumber}.jsonl`);
|
|
7
|
+
const line = `${JSON.stringify(event)}\n`;
|
|
8
|
+
await fs.appendFile(eventFilePath, line, 'utf8');
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/harness/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAwE7B,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,cAAsB,EACtB,UAAkB,EAClB,KAAmB;IAEnB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC9D,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/C,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,UAAU,QAAQ,CAAC,CAAC;IAClE,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;IAC1C,MAAM,EAAE,CAAC,UAAU,CAAC,aAAa,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACnD,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC5E,YAAY,EACV,SAAS,EACT,SAAS,EACT,cAAc,EACd,SAAS,EACT,SAAS,EACT,QAAQ,EACR,WAAW,EACX,aAAa,GACd,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { OsqConfig } from '../core/config.js';
|
|
2
|
+
export declare function applyDelta(projectRoot: string, specFolderPath: string, config: OsqConfig): Promise<void>;
|
|
3
|
+
export declare function archiveSpecFolder(projectRoot: string, specFolderPath: string, config: OsqConfig): Promise<string>;
|
|
4
|
+
export declare function checkAndArchiveSpec(projectRoot: string, specFolderPath: string, config: OsqConfig): Promise<boolean>;
|
|
5
|
+
//# sourceMappingURL=archiver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archiver.d.ts","sourceRoot":"","sources":["../../src/watcher/archiver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAInD,wBAAsB,UAAU,CAC9B,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,SAAS,GAChB,OAAO,CAAC,IAAI,CAAC,CAqCf;AAED,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,SAAS,GAChB,OAAO,CAAC,MAAM,CAAC,CAoBjB;AAED,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,SAAS,GAChB,OAAO,CAAC,OAAO,CAAC,CASlB"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { parseSpecMd } from '../core/parser.js';
|
|
4
|
+
import { deriveSpecState } from '../core/state.js';
|
|
5
|
+
export async function applyDelta(projectRoot, specFolderPath, config) {
|
|
6
|
+
const specMdPath = path.join(specFolderPath, 'spec.md');
|
|
7
|
+
let specContent = '';
|
|
8
|
+
try {
|
|
9
|
+
specContent = await fs.readFile(specMdPath, 'utf8');
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const specData = parseSpecMd(specContent);
|
|
15
|
+
if (!specData.delta.trim() || specData.features.writes.length === 0) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const featuresDir = path.join(projectRoot, config.paths.features);
|
|
19
|
+
await fs.mkdir(featuresDir, { recursive: true });
|
|
20
|
+
for (const featureName of specData.features.writes) {
|
|
21
|
+
const featurePath = path.join(featuresDir, `${featureName}.md`);
|
|
22
|
+
let existing = '';
|
|
23
|
+
try {
|
|
24
|
+
existing = await fs.readFile(featurePath, 'utf8');
|
|
25
|
+
}
|
|
26
|
+
catch { }
|
|
27
|
+
let updatedContent = '';
|
|
28
|
+
if (!existing.trim()) {
|
|
29
|
+
const titleWords = featureName
|
|
30
|
+
.split('-')
|
|
31
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
32
|
+
.join(' ');
|
|
33
|
+
updatedContent = `# ${titleWords}\n\n${specData.delta}\n`;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
updatedContent = `${existing.trim()}\n\n## Delta from ${specData.title}\n\n${specData.delta}\n`;
|
|
37
|
+
}
|
|
38
|
+
await fs.writeFile(featurePath, updatedContent, 'utf8');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export async function archiveSpecFolder(projectRoot, specFolderPath, config) {
|
|
42
|
+
const archiveDir = path.join(projectRoot, config.paths.archive);
|
|
43
|
+
await fs.mkdir(archiveDir, { recursive: true });
|
|
44
|
+
const folderName = path.basename(specFolderPath);
|
|
45
|
+
let targetPath = path.join(archiveDir, folderName);
|
|
46
|
+
let counter = 1;
|
|
47
|
+
while (await fs
|
|
48
|
+
.stat(targetPath)
|
|
49
|
+
.then(() => true)
|
|
50
|
+
.catch(() => false)) {
|
|
51
|
+
targetPath = path.join(archiveDir, `${folderName}-${counter}`);
|
|
52
|
+
counter++;
|
|
53
|
+
}
|
|
54
|
+
await fs.rename(specFolderPath, targetPath);
|
|
55
|
+
return targetPath;
|
|
56
|
+
}
|
|
57
|
+
export async function checkAndArchiveSpec(projectRoot, specFolderPath, config) {
|
|
58
|
+
const specState = await deriveSpecState(projectRoot, specFolderPath);
|
|
59
|
+
if (specState.status !== 'done') {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
await applyDelta(projectRoot, specFolderPath, config);
|
|
63
|
+
await archiveSpecFolder(projectRoot, specFolderPath, config);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=archiver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archiver.js","sourceRoot":"","sources":["../../src/watcher/archiver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { OsqConfig } from '../core/config.js';
|
|
2
|
+
import { type Logger } from '../core/logger.js';
|
|
3
|
+
import type { HarnessAdapter } from '../harness/types.js';
|
|
4
|
+
export interface WatcherSummary {
|
|
5
|
+
tasksRun: number;
|
|
6
|
+
specsArchived: number;
|
|
7
|
+
}
|
|
8
|
+
export interface StartWatcherOptions {
|
|
9
|
+
once?: boolean;
|
|
10
|
+
pollIntervalMs?: number;
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
logger?: Logger;
|
|
13
|
+
}
|
|
14
|
+
export interface ArchivedSpecSummary {
|
|
15
|
+
id: string;
|
|
16
|
+
folder: string;
|
|
17
|
+
archivedAt: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function formatAgo(ms: number): string;
|
|
20
|
+
/**
|
|
21
|
+
* Single idle status line: the watched spec directory, how many approved specs
|
|
22
|
+
* are still waiting, and when the last spec was archived. Pure so the exact
|
|
23
|
+
* shape is testable without a watcher.
|
|
24
|
+
*/
|
|
25
|
+
export declare function formatIdleStatus(specsDir: string, approvedWaiting: number, lastArchived?: ArchivedSpecSummary, now?: number): string;
|
|
26
|
+
/**
|
|
27
|
+
* Most recently archived spec folder, derived from the archive directory's
|
|
28
|
+
* modification times so the idle status survives a watcher restart.
|
|
29
|
+
*/
|
|
30
|
+
export declare function findLatestArchivedSpec(projectRoot: string, config: OsqConfig): Promise<ArchivedSpecSummary | undefined>;
|
|
31
|
+
export declare function runWatcherCycle(projectRoot: string, config: OsqConfig, adapter: HarnessAdapter, logger?: Logger): Promise<WatcherSummary>;
|
|
32
|
+
export declare function runWatcherOnce(projectRoot: string, config: OsqConfig, adapter: HarnessAdapter, logger?: Logger): Promise<WatcherSummary>;
|
|
33
|
+
export declare function startWatcher(projectRoot: string, config: OsqConfig, adapter: HarnessAdapter, options?: StartWatcherOptions): Promise<void>;
|
|
34
|
+
//# sourceMappingURL=loop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/watcher/loop.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,mBAAmB,CAAC;AAG/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAO1D,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAS5C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EACvB,YAAY,CAAC,EAAE,mBAAmB,EAClC,GAAG,GAAE,MAAmB,GACvB,MAAM,CAKR;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,SAAS,GAChB,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAyB1C;AAUD,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,cAAc,EACvB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,cAAc,CAAC,CAmFzB;AAED,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,cAAc,EACvB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,cAAc,CAAC,CAezB;AAED,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,IAAI,CAAC,CAuFf"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { watch } from 'chokidar';
|
|
4
|
+
import { reapStaleLocks } from '../core/lock.js';
|
|
5
|
+
import { resolveSymbol } from '../core/logger.js';
|
|
6
|
+
import { compareNumericPrefix, deriveSpecState } from '../core/state.js';
|
|
7
|
+
import { preflightOpencode } from '../harness/opencode.js';
|
|
8
|
+
import { checkAndArchiveSpec } from './archiver.js';
|
|
9
|
+
import { runTask } from './runner.js';
|
|
10
|
+
const SHOW_CURSOR = '\x1b[?25h';
|
|
11
|
+
const EXIT_SIGINT = 130;
|
|
12
|
+
export function formatAgo(ms) {
|
|
13
|
+
const seconds = Math.max(0, Math.floor(ms / 1000));
|
|
14
|
+
if (seconds < 60)
|
|
15
|
+
return `${seconds}s ago`;
|
|
16
|
+
const minutes = Math.floor(seconds / 60);
|
|
17
|
+
if (minutes < 60)
|
|
18
|
+
return `${minutes}m ago`;
|
|
19
|
+
const hours = Math.floor(minutes / 60);
|
|
20
|
+
if (hours < 24)
|
|
21
|
+
return `${hours}h ago`;
|
|
22
|
+
const days = Math.floor(hours / 24);
|
|
23
|
+
return `${days}d ago`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Single idle status line: the watched spec directory, how many approved specs
|
|
27
|
+
* are still waiting, and when the last spec was archived. Pure so the exact
|
|
28
|
+
* shape is testable without a watcher.
|
|
29
|
+
*/
|
|
30
|
+
export function formatIdleStatus(specsDir, approvedWaiting, lastArchived, now = Date.now()) {
|
|
31
|
+
const last = lastArchived
|
|
32
|
+
? `last: ${lastArchived.id} archived ${formatAgo(now - lastArchived.archivedAt)}`
|
|
33
|
+
: 'last: none';
|
|
34
|
+
return `watching ${specsDir} · ${approvedWaiting} approved waiting · ${last}`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Most recently archived spec folder, derived from the archive directory's
|
|
38
|
+
* modification times so the idle status survives a watcher restart.
|
|
39
|
+
*/
|
|
40
|
+
export async function findLatestArchivedSpec(projectRoot, config) {
|
|
41
|
+
const archiveDir = path.join(projectRoot, config.paths.archive);
|
|
42
|
+
let entries = [];
|
|
43
|
+
try {
|
|
44
|
+
entries = await fs.readdir(archiveDir, { withFileTypes: true });
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
let latest;
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
if (!entry.isDirectory())
|
|
52
|
+
continue;
|
|
53
|
+
const stat = await fs.stat(path.join(archiveDir, entry.name)).catch(() => null);
|
|
54
|
+
if (!stat)
|
|
55
|
+
continue;
|
|
56
|
+
const summary = {
|
|
57
|
+
id: entry.name.match(/^(\d+)/)?.[1] ?? entry.name,
|
|
58
|
+
folder: entry.name,
|
|
59
|
+
archivedAt: stat.mtimeMs,
|
|
60
|
+
};
|
|
61
|
+
if (!latest || summary.archivedAt > latest.archivedAt) {
|
|
62
|
+
latest = summary;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return latest;
|
|
66
|
+
}
|
|
67
|
+
function errorMessage(err) {
|
|
68
|
+
return err instanceof Error ? err.message : String(err);
|
|
69
|
+
}
|
|
70
|
+
function logWatcherError(logger, useSymbols, err) {
|
|
71
|
+
logger?.error(`${resolveSymbol('✗', '[error]', useSymbols)} watcher error: ${errorMessage(err)}`);
|
|
72
|
+
}
|
|
73
|
+
export async function runWatcherCycle(projectRoot, config, adapter, logger) {
|
|
74
|
+
const useSymbols = logger?.symbols === true;
|
|
75
|
+
const tag = (symbol, word) => resolveSymbol(symbol, word, useSymbols);
|
|
76
|
+
const specsDir = path.join(projectRoot, config.paths.specs);
|
|
77
|
+
let entries = [];
|
|
78
|
+
try {
|
|
79
|
+
entries = await fs.readdir(specsDir);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
logger?.status(formatIdleStatus(config.paths.specs, 0));
|
|
83
|
+
return { tasksRun: 0, specsArchived: 0 };
|
|
84
|
+
}
|
|
85
|
+
const specFolders = entries
|
|
86
|
+
.filter((e) => !e.startsWith('_') && e !== 'archive')
|
|
87
|
+
.sort(compareNumericPrefix);
|
|
88
|
+
let tasksRun = 0;
|
|
89
|
+
let specsArchived = 0;
|
|
90
|
+
let approvedWaiting = 0;
|
|
91
|
+
for (const folder of specFolders) {
|
|
92
|
+
const folderPath = path.join(specsDir, folder);
|
|
93
|
+
try {
|
|
94
|
+
const stat = await fs.stat(folderPath).catch(() => null);
|
|
95
|
+
if (!stat || !stat.isDirectory())
|
|
96
|
+
continue;
|
|
97
|
+
await reapStaleLocks(folderPath, config.timeouts.staleLockSeconds);
|
|
98
|
+
const specState = await deriveSpecState(projectRoot, folderPath);
|
|
99
|
+
if (specState.approvedHash &&
|
|
100
|
+
(specState.status === 'pending' ||
|
|
101
|
+
specState.status === 'running' ||
|
|
102
|
+
specState.status === 'blocked')) {
|
|
103
|
+
approvedWaiting++;
|
|
104
|
+
}
|
|
105
|
+
if (specState.status === 'pending' && specState.nextTask) {
|
|
106
|
+
const taskNumber = specState.nextTask.taskNumber;
|
|
107
|
+
logger?.info(`${tag('▶ spec', '[spec]')} ${specState.id} picked up (${folder})`);
|
|
108
|
+
const taskResult = await runTask(projectRoot, folderPath, taskNumber, config, adapter, logger);
|
|
109
|
+
tasksRun++;
|
|
110
|
+
if (taskResult.success) {
|
|
111
|
+
const archived = await checkAndArchiveSpec(projectRoot, folderPath, config);
|
|
112
|
+
if (archived) {
|
|
113
|
+
specsArchived++;
|
|
114
|
+
logger?.info(`${tag('✓ spec', '[archived]')} ${specState.id} archived (${folder})`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
logger?.info(`${tag('■ spec', '[halted]')} ${specState.id} halted (task ${taskNumber} dead)`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else if (specState.status === 'done') {
|
|
122
|
+
const archived = await checkAndArchiveSpec(projectRoot, folderPath, config);
|
|
123
|
+
if (archived) {
|
|
124
|
+
specsArchived++;
|
|
125
|
+
logger?.info(`${tag('✓ spec', '[archived]')} ${specState.id} archived (${folder})`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
logWatcherError(logger, useSymbols, err);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (tasksRun === 0) {
|
|
134
|
+
const lastArchived = await findLatestArchivedSpec(projectRoot, config).catch(() => undefined);
|
|
135
|
+
logger?.status(formatIdleStatus(config.paths.specs, approvedWaiting, lastArchived));
|
|
136
|
+
}
|
|
137
|
+
return { tasksRun, specsArchived };
|
|
138
|
+
}
|
|
139
|
+
export async function runWatcherOnce(projectRoot, config, adapter, logger) {
|
|
140
|
+
let totalTasksRun = 0;
|
|
141
|
+
let totalSpecsArchived = 0;
|
|
142
|
+
while (true) {
|
|
143
|
+
const cycle = await runWatcherCycle(projectRoot, config, adapter, logger);
|
|
144
|
+
totalTasksRun += cycle.tasksRun;
|
|
145
|
+
totalSpecsArchived += cycle.specsArchived;
|
|
146
|
+
if (cycle.tasksRun === 0) {
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return { tasksRun: totalTasksRun, specsArchived: totalSpecsArchived };
|
|
151
|
+
}
|
|
152
|
+
export async function startWatcher(projectRoot, config, adapter, options = {}) {
|
|
153
|
+
const logger = options.logger;
|
|
154
|
+
if (adapter.name === 'opencode' || config.harness === 'opencode') {
|
|
155
|
+
if (adapter.preflight) {
|
|
156
|
+
await adapter.preflight(projectRoot, config);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
await preflightOpencode(projectRoot, config);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (options.once) {
|
|
163
|
+
await runWatcherOnce(projectRoot, config, adapter, logger);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const useSymbols = logger?.symbols === true;
|
|
167
|
+
let interrupted = false;
|
|
168
|
+
let stopped = false;
|
|
169
|
+
let isRunningCycle = false;
|
|
170
|
+
let intervalId = null;
|
|
171
|
+
let watcher = null;
|
|
172
|
+
let onSigint = () => { };
|
|
173
|
+
const stop = () => {
|
|
174
|
+
if (stopped)
|
|
175
|
+
return;
|
|
176
|
+
stopped = true;
|
|
177
|
+
if (intervalId)
|
|
178
|
+
clearInterval(intervalId);
|
|
179
|
+
watcher?.close().catch(() => { });
|
|
180
|
+
process.removeListener('SIGINT', onSigint);
|
|
181
|
+
};
|
|
182
|
+
onSigint = () => {
|
|
183
|
+
if (interrupted) {
|
|
184
|
+
process.exit(EXIT_SIGINT);
|
|
185
|
+
}
|
|
186
|
+
interrupted = true;
|
|
187
|
+
logger?.clearStatus();
|
|
188
|
+
if (logger?.interactive) {
|
|
189
|
+
process.stderr.write(SHOW_CURSOR);
|
|
190
|
+
}
|
|
191
|
+
logger?.info('waiting for running task to exit (press Ctrl+C again to kill)');
|
|
192
|
+
if (!isRunningCycle) {
|
|
193
|
+
stop();
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
process.on('SIGINT', onSigint);
|
|
197
|
+
const cycleHandler = async () => {
|
|
198
|
+
if (interrupted || stopped || isRunningCycle)
|
|
199
|
+
return;
|
|
200
|
+
isRunningCycle = true;
|
|
201
|
+
try {
|
|
202
|
+
await runWatcherCycle(projectRoot, config, adapter, logger);
|
|
203
|
+
}
|
|
204
|
+
catch (err) {
|
|
205
|
+
logWatcherError(logger, useSymbols, err);
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
isRunningCycle = false;
|
|
209
|
+
if (interrupted)
|
|
210
|
+
stop();
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
if (options.signal) {
|
|
214
|
+
if (options.signal.aborted) {
|
|
215
|
+
stop();
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
options.signal.addEventListener('abort', stop);
|
|
219
|
+
}
|
|
220
|
+
await cycleHandler();
|
|
221
|
+
if (interrupted || stopped)
|
|
222
|
+
return;
|
|
223
|
+
const specsDir = path.join(projectRoot, config.paths.specs);
|
|
224
|
+
watcher = watch(specsDir, {
|
|
225
|
+
ignoreInitial: true,
|
|
226
|
+
depth: 3,
|
|
227
|
+
});
|
|
228
|
+
watcher.on('all', () => {
|
|
229
|
+
cycleHandler().catch(() => { });
|
|
230
|
+
});
|
|
231
|
+
intervalId = setInterval(() => {
|
|
232
|
+
cycleHandler().catch(() => { });
|
|
233
|
+
}, options.pollIntervalMs || 1000);
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop.js","sourceRoot":"","sources":["../../src/watcher/loop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { OsqConfig } from '../core/config.js';
|
|
2
|
+
import { type Logger } from '../core/logger.js';
|
|
3
|
+
import { type HarnessAdapter } from '../harness/types.js';
|
|
4
|
+
export type RunTaskFailureReason = 'spec_conflict' | 'already_running' | 'no_result' | 'verify_red' | 'crashed' | 'timeout';
|
|
5
|
+
export interface RunTaskResult {
|
|
6
|
+
success: boolean;
|
|
7
|
+
reason?: RunTaskFailureReason;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Single-line, human-readable summary of a task outcome. Kept pure so the exact
|
|
12
|
+
* shape can be tested without running the runner, and used directly beside every
|
|
13
|
+
* marker write (`done/<n>` or `dead/<n>.md`) so the log and the marker agree.
|
|
14
|
+
*/
|
|
15
|
+
export declare function formatTaskOutcomeSummary(taskNumber: string, success: boolean, reason?: RunTaskFailureReason, extra?: string): string;
|
|
16
|
+
export declare function tickTaskCheckbox(specFolderPath: string, taskNumber: string): Promise<void>;
|
|
17
|
+
export interface TaskHeartbeatStats {
|
|
18
|
+
elapsedSeconds: number;
|
|
19
|
+
eventCount: number;
|
|
20
|
+
totalTokens: number;
|
|
21
|
+
toolCount: number;
|
|
22
|
+
cost?: number;
|
|
23
|
+
lastToolSummary?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Snapshot of task progress. Counters are maintained in memory and advanced
|
|
27
|
+
* with only the bytes appended since the previous poll; the event file may not
|
|
28
|
+
* exist yet, in which case counters are zero.
|
|
29
|
+
*/
|
|
30
|
+
export declare function computeTaskHeartbeatStats(specFolderPath: string, taskNumber: string, startTime: number): Promise<TaskHeartbeatStats>;
|
|
31
|
+
/**
|
|
32
|
+
* Drop the in-memory counters for a finished task so the map does not grow
|
|
33
|
+
* across watcher cycles.
|
|
34
|
+
*/
|
|
35
|
+
export declare function clearTaskHeartbeatStats(specFolderPath: string, taskNumber: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* Single-line running status row. The logger owns the animated spinner and
|
|
38
|
+
* prepends `<frame> `, so this text is truncated to leave room for that prefix
|
|
39
|
+
* while fitting `terminalWidth` columns.
|
|
40
|
+
*/
|
|
41
|
+
export declare function formatTaskStatusRow(taskNumber: string, stats: TaskHeartbeatStats, terminalWidth?: number): string;
|
|
42
|
+
/** Periodic heartbeat line shared by the 1s TTY row and the non-TTY log. */
|
|
43
|
+
export declare function formatTaskHeartbeatLine(taskNumber: string, stats: TaskHeartbeatStats): string;
|
|
44
|
+
/**
|
|
45
|
+
* Curated task-started line: a single info line carrying the task title
|
|
46
|
+
* truncated so the whole line fits the terminal width. The lifecycle details
|
|
47
|
+
* (pid, timeout) travel with it so the marker and the log line stay in sync.
|
|
48
|
+
*/
|
|
49
|
+
export declare function formatTaskStartedLine(taskNumber: string, title: string, pid: number | undefined, timeoutSeconds: number, symbols: boolean, terminalWidth?: number): string;
|
|
50
|
+
/**
|
|
51
|
+
* Curated task-outcome line: verified or dead with the failure reason and the
|
|
52
|
+
* elapsed seconds since the task began.
|
|
53
|
+
*/
|
|
54
|
+
export declare function formatTaskOutcomeLine(taskNumber: string, success: boolean, reason: RunTaskFailureReason | undefined, elapsedSeconds: number, symbols: boolean): string;
|
|
55
|
+
/**
|
|
56
|
+
* Return the last text message emitted on the agent's event stream, or null
|
|
57
|
+
* when the stream has no text at all. Synthesis reads exclusively from
|
|
58
|
+
* first-class `text` events carrying the completed assistant message; raw
|
|
59
|
+
* harness payload shapes are never inspected. The stream is the append-only
|
|
60
|
+
* `.run/events/<n>.jsonl` file, so this survives a watcher restart.
|
|
61
|
+
*/
|
|
62
|
+
export declare function extractFinalTextFromStream(specFolderPath: string, taskNumber: string): Promise<string | null>;
|
|
63
|
+
/**
|
|
64
|
+
* Write a result file synthesized from the agent's final stream message. The
|
|
65
|
+
* frontmatter flags it as `synthesized: true` and a comment attributes it to
|
|
66
|
+
* the watcher so a reader never mistakes it for an agent-authored result.
|
|
67
|
+
*/
|
|
68
|
+
export declare function synthesizeResultFile(resultsDir: string, taskNumber: string, finalText: string): Promise<string>;
|
|
69
|
+
export declare function runTask(projectRoot: string, specFolderPath: string, taskNumber: string, config: OsqConfig, adapter: HarnessAdapter, logger?: Logger): Promise<RunTaskResult>;
|
|
70
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/watcher/runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAGnD,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,mBAAmB,CAAC;AAG/D,OAAO,EAGL,KAAK,cAAc,EAGpB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,oBAAoB,GAC5B,eAAe,GACf,iBAAiB,GACjB,WAAW,GACX,YAAY,GACZ,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,MAAM,CAAC,EAAE,oBAAoB,EAC7B,KAAK,CAAC,EAAE,MAAM,GACb,MAAM,CAOR;AAED,wBAAsB,gBAAgB,CAAC,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAehG;AAmDD,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAuBD;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,CAAC,CA0E7B;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAGxF;AAkBD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,kBAAkB,EACzB,aAAa,GAAE,MAA0E,GACxF,MAAM,CAwBR;AAED,4EAA4E;AAC5E,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAE7F;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,OAAO,EAChB,aAAa,GAAE,MAA0E,GACxF,MAAM,CAKR;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,oBAAoB,GAAG,SAAS,EACxC,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,OAAO,GACf,MAAM,CAKR;AAED;;;;;;GAMG;AACH,wBAAsB,0BAA0B,CAC9C,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgCxB;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED,wBAAsB,OAAO,CAC3B,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,cAAc,EACvB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,aAAa,CAAC,CAmYxB"}
|