@kodrunhq/opencode-autopilot 1.4.0 → 1.5.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.
@@ -0,0 +1,116 @@
1
+ import { execFile as execFileCb } from "node:child_process";
2
+ import { basename } from "node:path";
3
+ import { promisify } from "node:util";
4
+ import { tool } from "@opencode-ai/plugin";
5
+
6
+ const execFile = promisify(execFileCb);
7
+
8
+ interface UpdateDocsArgs {
9
+ readonly scope?: string;
10
+ }
11
+
12
+ interface AffectedDoc {
13
+ readonly doc: string;
14
+ readonly reason: string;
15
+ readonly suggestion: string;
16
+ }
17
+
18
+ /** Run a git command and return stdout lines (empty array on error). */
19
+ async function gitLines(args: readonly string[], cwd: string): Promise<readonly string[]> {
20
+ try {
21
+ const { stdout } = await execFile("git", [...args], { cwd });
22
+ return stdout
23
+ .trim()
24
+ .split("\n")
25
+ .filter((line) => line.length > 0);
26
+ } catch {
27
+ return [];
28
+ }
29
+ }
30
+
31
+ export async function updateDocsCore(args: UpdateDocsArgs, projectDir: string): Promise<string> {
32
+ const scope = args.scope ?? "changed";
33
+
34
+ // Get changed source files
35
+ const changedFiles: readonly string[] =
36
+ scope === "all"
37
+ ? await gitLines(["ls-files"], projectDir)
38
+ : await gitLines(["diff", "--name-only", "HEAD"], projectDir);
39
+
40
+ // Get all markdown files in the project
41
+ const mdFiles = await gitLines(["ls-files", "*.md"], projectDir);
42
+
43
+ // For each changed source file, check if any markdown file references it
44
+ const affectedDocs: AffectedDoc[] = [];
45
+ const seenDocs = new Set<string>();
46
+
47
+ for (const changedFile of changedFiles) {
48
+ // Skip markdown files themselves
49
+ if (changedFile.endsWith(".md")) continue;
50
+
51
+ const fileBaseName = basename(changedFile);
52
+ // Strip extension for module-style references
53
+ const moduleName = fileBaseName.replace(/\.[^.]+$/, "");
54
+
55
+ for (const mdFile of mdFiles) {
56
+ if (seenDocs.has(`${mdFile}:${changedFile}`)) continue;
57
+
58
+ // Simple heuristic: check if the markdown file path suggests it documents this area
59
+ // or if the changed file's name/module appears in common documentation patterns
60
+ const mdBaseName = basename(mdFile).replace(/\.md$/, "").toLowerCase();
61
+ const changedDir = changedFile.split("/").slice(0, -1).join("/");
62
+
63
+ const isRelated =
64
+ mdBaseName === "readme" ||
65
+ mdBaseName === moduleName.toLowerCase() ||
66
+ (changedDir.length > 0 && mdFile.toLowerCase().includes(changedDir.toLowerCase()));
67
+
68
+ if (isRelated) {
69
+ seenDocs.add(`${mdFile}:${changedFile}`);
70
+ affectedDocs.push({
71
+ doc: mdFile,
72
+ reason: `may be related to ${changedFile} (heuristic: path/name match)`,
73
+ suggestion: `Review ${mdFile} — it may need updates to reflect changes to ${changedFile}`,
74
+ });
75
+ }
76
+ }
77
+ }
78
+
79
+ // Deduplicate by doc path
80
+ const uniqueDocs = Array.from(
81
+ affectedDocs
82
+ .reduce((map, item) => {
83
+ if (!map.has(item.doc)) {
84
+ map.set(item.doc, item);
85
+ }
86
+ return map;
87
+ }, new Map<string, AffectedDoc>())
88
+ .values(),
89
+ );
90
+
91
+ const nonMdChanged = changedFiles.filter((f) => !f.endsWith(".md"));
92
+
93
+ return JSON.stringify(
94
+ {
95
+ changedFiles: nonMdChanged,
96
+ affectedDocs: uniqueDocs,
97
+ summary: `${nonMdChanged.length} source files changed, ${uniqueDocs.length} docs may need updates`,
98
+ },
99
+ null,
100
+ 2,
101
+ );
102
+ }
103
+
104
+ export const ocUpdateDocs = tool({
105
+ description: "Detect documentation affected by recent code changes and suggest updates.",
106
+ args: {
107
+ scope: tool.schema
108
+ .enum(["changed", "all"])
109
+ .optional()
110
+ .default("changed")
111
+ .describe("Scope: 'changed' for git diff, 'all' for full scan"),
112
+ },
113
+ async execute(args) {
114
+ return updateDocsCore(args, process.cwd());
115
+ },
116
+ });