@h-rig/task-sources-plugin 0.0.6-alpha.156
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/src/files-source.d.ts +18 -0
- package/dist/src/files-source.js +107 -0
- package/dist/src/github-issues-source.d.ts +63 -0
- package/dist/src/github-issues-source.js +843 -0
- package/dist/src/plugin.d.ts +17 -0
- package/dist/src/plugin.js +1077 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @h-rig/task-sources-plugin
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RegisteredTaskSource } from "@rig/contracts";
|
|
2
|
+
export interface FilesTaskSourceOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Directory containing one JSON file per task. Use either `path` (matches
|
|
5
|
+
* the `taskSource.path` field in rig.config.ts) or `dir` (back-compat).
|
|
6
|
+
*/
|
|
7
|
+
path?: string;
|
|
8
|
+
dir?: string;
|
|
9
|
+
pattern?: RegExp;
|
|
10
|
+
/**
|
|
11
|
+
* Root a relative `path`/`dir` resolves against. The serving process's cwd
|
|
12
|
+
* is NOT the project (workspace-spawned servers run from the engine
|
|
13
|
+
* checkout), so without this a relative path silently reads the WRONG
|
|
14
|
+
* repo's tasks. Defaults to cwd for direct programmatic use.
|
|
15
|
+
*/
|
|
16
|
+
projectRoot?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function createFilesTaskSource(opts: FilesTaskSourceOptions): RegisteredTaskSource;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/task-sources-plugin/src/files-source.ts
|
|
3
|
+
import { readFileSync, readdirSync, existsSync, statSync, writeFileSync } from "fs";
|
|
4
|
+
import { join, basename, isAbsolute, resolve } from "path";
|
|
5
|
+
var DEFAULT_PATTERN = /\.(task\.)?json$/;
|
|
6
|
+
function readTaskFile(file, pattern) {
|
|
7
|
+
const raw = JSON.parse(readFileSync(file, "utf-8"));
|
|
8
|
+
const inferredId = basename(file).replace(pattern, "");
|
|
9
|
+
const labels = Array.isArray(raw.labels) ? raw.labels.filter((label) => typeof label === "string") : [];
|
|
10
|
+
const scope = labels.filter((label) => label.startsWith("scope:")).map((label) => label.slice("scope:".length));
|
|
11
|
+
const validators = labels.filter((label) => label.startsWith("validator:")).map((label) => label.slice("validator:".length));
|
|
12
|
+
const roleLabel = labels.find((label) => label.startsWith("role:"));
|
|
13
|
+
return {
|
|
14
|
+
id: raw["id"] ?? inferredId,
|
|
15
|
+
deps: raw["deps"] ?? raw["depends_on"] ?? [],
|
|
16
|
+
status: raw["status"] ?? "ready",
|
|
17
|
+
...scope.length > 0 ? { scope } : {},
|
|
18
|
+
...roleLabel ? { role: roleLabel.slice("role:".length) } : {},
|
|
19
|
+
...validators.length > 0 ? { validators, validation: validators } : {},
|
|
20
|
+
...raw
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function createFilesTaskSource(opts) {
|
|
24
|
+
const pattern = opts.pattern ?? DEFAULT_PATTERN;
|
|
25
|
+
const configured = opts.path ?? opts.dir;
|
|
26
|
+
if (!configured) {
|
|
27
|
+
throw new Error("createFilesTaskSource: either `path` or `dir` must be provided");
|
|
28
|
+
}
|
|
29
|
+
const directory = isAbsolute(configured) ? configured : resolve(opts.projectRoot ?? process.cwd(), configured);
|
|
30
|
+
const findTaskFile = (id) => {
|
|
31
|
+
if (!existsSync(directory))
|
|
32
|
+
return;
|
|
33
|
+
for (const name of readdirSync(directory)) {
|
|
34
|
+
if (!pattern.test(name))
|
|
35
|
+
continue;
|
|
36
|
+
const p = join(directory, name);
|
|
37
|
+
try {
|
|
38
|
+
if (!statSync(p).isFile())
|
|
39
|
+
continue;
|
|
40
|
+
if (readTaskFile(p, pattern).id === id)
|
|
41
|
+
return p;
|
|
42
|
+
} catch {}
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
};
|
|
46
|
+
const applyUpdate = (id, update) => {
|
|
47
|
+
const file = findTaskFile(id);
|
|
48
|
+
if (!file) {
|
|
49
|
+
throw new Error(`files task not found: ${id}`);
|
|
50
|
+
}
|
|
51
|
+
const raw = JSON.parse(readFileSync(file, "utf-8"));
|
|
52
|
+
if (update.status)
|
|
53
|
+
raw.status = update.status;
|
|
54
|
+
if (update.title !== undefined)
|
|
55
|
+
raw.title = update.title;
|
|
56
|
+
if (update.body !== undefined)
|
|
57
|
+
raw.body = update.body;
|
|
58
|
+
if (update.comment?.trim()) {
|
|
59
|
+
const existing = Array.isArray(raw.comments) ? raw.comments : [];
|
|
60
|
+
raw.comments = [
|
|
61
|
+
...existing,
|
|
62
|
+
{
|
|
63
|
+
body: update.comment,
|
|
64
|
+
createdAt: new Date().toISOString(),
|
|
65
|
+
source: "rig"
|
|
66
|
+
}
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
writeFileSync(file, `${JSON.stringify(raw, null, 2)}
|
|
70
|
+
`, "utf-8");
|
|
71
|
+
};
|
|
72
|
+
return {
|
|
73
|
+
id: "std:files",
|
|
74
|
+
kind: "files",
|
|
75
|
+
async list() {
|
|
76
|
+
if (!existsSync(directory))
|
|
77
|
+
return [];
|
|
78
|
+
const out = [];
|
|
79
|
+
for (const name of readdirSync(directory)) {
|
|
80
|
+
if (!pattern.test(name))
|
|
81
|
+
continue;
|
|
82
|
+
const p = join(directory, name);
|
|
83
|
+
try {
|
|
84
|
+
if (!statSync(p).isFile())
|
|
85
|
+
continue;
|
|
86
|
+
out.push(readTaskFile(p, pattern));
|
|
87
|
+
} catch (err) {
|
|
88
|
+
console.warn(`[files-source] skipped ${name}: ${err.message}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
},
|
|
93
|
+
async get(id) {
|
|
94
|
+
const all = await this.list();
|
|
95
|
+
return all.find((t) => t.id === id);
|
|
96
|
+
},
|
|
97
|
+
async updateStatus(id, status) {
|
|
98
|
+
applyUpdate(id, { status });
|
|
99
|
+
},
|
|
100
|
+
async updateTask(id, update) {
|
|
101
|
+
applyUpdate(id, update);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export {
|
|
106
|
+
createFilesTaskSource
|
|
107
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import type { GitHubCredentialProvider, RegisteredTaskSource, TaskRecord } from "@rig/contracts";
|
|
3
|
+
export type { GitHubCredentialPurpose, GitHubCredentialProvider } from "@rig/contracts";
|
|
4
|
+
export type GitHubIssueUpdatesMode = "lifecycle" | "minimal" | "off";
|
|
5
|
+
export type GitHubProjectLifecycleStatus = "todo" | "running" | "prOpen" | "ciFixing" | "merging" | "done" | "needsAttention";
|
|
6
|
+
export interface GitHubProjectsOptions {
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
projectId?: string;
|
|
9
|
+
statusFieldId?: string;
|
|
10
|
+
statuses?: Partial<Record<GitHubProjectLifecycleStatus, string>>;
|
|
11
|
+
}
|
|
12
|
+
export interface GitHubIssuesOptions {
|
|
13
|
+
owner: string;
|
|
14
|
+
repo: string;
|
|
15
|
+
labels?: readonly string[];
|
|
16
|
+
state?: "open" | "closed" | "all";
|
|
17
|
+
assignee?: string;
|
|
18
|
+
ghBinary?: string;
|
|
19
|
+
workspaceId?: string;
|
|
20
|
+
userId?: string;
|
|
21
|
+
credentialProvider?: GitHubCredentialProvider;
|
|
22
|
+
issueUpdates?: GitHubIssueUpdatesMode;
|
|
23
|
+
/** Timeout for every gh CLI call. Defaults to 15 seconds. */
|
|
24
|
+
timeoutMs?: number;
|
|
25
|
+
/** Maximum issue-list rows before Rig fails loudly instead of silently truncating. Defaults to 1,000. */
|
|
26
|
+
listLimit?: number;
|
|
27
|
+
/** @internal — for testing. Override the spawnSync used by the adapter. */
|
|
28
|
+
spawn?: typeof spawnSync;
|
|
29
|
+
/** Notify the host that issue-backed task state changed and snapshots should refresh. */
|
|
30
|
+
onTaskChanged?: (event: {
|
|
31
|
+
repo: string;
|
|
32
|
+
id: string;
|
|
33
|
+
status?: string;
|
|
34
|
+
reason: "github-issue-updated";
|
|
35
|
+
}) => void;
|
|
36
|
+
/** Optional GitHub Projects (v2) status-field sync mapped from Rig task status. */
|
|
37
|
+
projects?: GitHubProjectsOptions;
|
|
38
|
+
/** Opt into GitHub-native issue dependency reads; body parsing remains the fallback. */
|
|
39
|
+
useNativeDependencies?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface GitHubIssueCreateInput {
|
|
42
|
+
title: string;
|
|
43
|
+
body?: string;
|
|
44
|
+
labels?: readonly string[];
|
|
45
|
+
}
|
|
46
|
+
export interface GitHubIssuesTaskSource extends RegisteredTaskSource {
|
|
47
|
+
addLabels(id: string, labels: readonly string[]): Promise<void>;
|
|
48
|
+
removeLabels(id: string, labels: readonly string[]): Promise<void>;
|
|
49
|
+
createIssue(input: GitHubIssueCreateInput): Promise<TaskRecord>;
|
|
50
|
+
getIssueBody(id: string): Promise<string | undefined>;
|
|
51
|
+
}
|
|
52
|
+
export declare const RIG_STATUS_COMMENT_MARKER = "<!-- rig:status-comment -->";
|
|
53
|
+
export declare const RIG_METADATA_START = "<!-- rig:metadata:start -->";
|
|
54
|
+
export declare const RIG_METADATA_END = "<!-- rig:metadata:end -->";
|
|
55
|
+
export declare function updateRigOwnedMetadataBlock(body: string, metadata: Record<string, unknown>): string;
|
|
56
|
+
export declare function buildRigStickyStatusComment(input: {
|
|
57
|
+
status: "running" | "prOpen" | "ciFixing" | "done" | "needsAttention" | string;
|
|
58
|
+
summary: string;
|
|
59
|
+
runId?: string;
|
|
60
|
+
prUrl?: string;
|
|
61
|
+
details?: readonly string[];
|
|
62
|
+
}): string;
|
|
63
|
+
export declare function createGitHubIssuesTaskSource(opts: GitHubIssuesOptions): GitHubIssuesTaskSource;
|