teamcopilot 0.1.3 → 0.1.4

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.
Files changed (22) hide show
  1. package/dist/frontend/assets/{cssMode-CV1QbDdV.js → cssMode-DHaUFfVN.js} +1 -1
  2. package/dist/frontend/assets/{freemarker2-SMKJ5iKX.js → freemarker2-CTVtohp4.js} +1 -1
  3. package/dist/frontend/assets/{handlebars-k8LxWzR3.js → handlebars-CI1GoG3L.js} +1 -1
  4. package/dist/frontend/assets/{html-ynahYm-q.js → html-C1Kdg4Tl.js} +1 -1
  5. package/dist/frontend/assets/{htmlMode-CKlgbgGX.js → htmlMode-CslwWXHV.js} +1 -1
  6. package/dist/frontend/assets/{index-CjZBHWet.js → index-CJMxNDke.js} +3 -3
  7. package/dist/frontend/assets/index-DrplZfJY.css +1 -0
  8. package/dist/frontend/assets/{javascript-3tohEroZ.js → javascript-D_jyP6QE.js} +1 -1
  9. package/dist/frontend/assets/{jsonMode-Bc561-dq.js → jsonMode-BT5z_9Wi.js} +1 -1
  10. package/dist/frontend/assets/{liquid-DwkCYmXC.js → liquid-CATjnK_z.js} +1 -1
  11. package/dist/frontend/assets/{mdx-BpoFi6BF.js → mdx-B4zdWJY_.js} +1 -1
  12. package/dist/frontend/assets/{python-BiLSr3a-.js → python-B_opZ_lQ.js} +1 -1
  13. package/dist/frontend/assets/{razor-BcYPJYvW.js → razor-BKopqk1g.js} +1 -1
  14. package/dist/frontend/assets/{tsMode-0Aumq3lP.js → tsMode-CMjXzcRc.js} +1 -1
  15. package/dist/frontend/assets/{typescript-C2VKs0MK.js → typescript-lbL28oip.js} +1 -1
  16. package/dist/frontend/assets/{xml-B19fG9u5.js → xml-BB8-GMIl.js} +1 -1
  17. package/dist/frontend/assets/{yaml-DFv9ENR5.js → yaml-B5S_93u4.js} +1 -1
  18. package/dist/frontend/index.html +2 -2
  19. package/dist/workspace_files/.opencode/plugins/apply-patch-session-diff.ts +175 -9
  20. package/dist/workspace_files/package.json +1 -1
  21. package/package.json +1 -1
  22. package/dist/frontend/assets/index-CDt94asq.css +0 -1
@@ -1,3 +1,4 @@
1
+ import fs from "node:fs"
1
2
  import path from "node:path"
2
3
  import type { Plugin } from "@opencode-ai/plugin"
3
4
 
@@ -17,6 +18,8 @@ interface SessionLookupResponse {
17
18
  }
18
19
  }
19
20
 
21
+ type ToolArgs = Record<string, unknown> | undefined
22
+
20
23
  function findPatchPayload(value: unknown): string | null {
21
24
  if (typeof value === "string") {
22
25
  const normalized = value.replace(/\r\n/g, "\n")
@@ -62,6 +65,118 @@ function extractTrackedPathsFromPatch(patchText: string): string[] {
62
65
  return Array.from(new Set(paths.filter((candidate) => candidate.length > 0)))
63
66
  }
64
67
 
68
+ function findNestedStringByKeys(value: unknown, keys: Set<string>): string | null {
69
+ if (!value || typeof value !== "object") {
70
+ return null
71
+ }
72
+
73
+ for (const [key, nestedValue] of Object.entries(value as Record<string, unknown>)) {
74
+ if (keys.has(key) && typeof nestedValue === "string" && nestedValue.trim().length > 0) {
75
+ return nestedValue
76
+ }
77
+
78
+ const nestedMatch = findNestedStringByKeys(nestedValue, keys)
79
+ if (nestedMatch) {
80
+ return nestedMatch
81
+ }
82
+ }
83
+
84
+ return null
85
+ }
86
+
87
+ function tokenizeCommand(command: string): string[] {
88
+ const tokens = command.match(/"[^"]*"|'[^']*'|&&|\|\||[;|]|[^\s]+/g) ?? []
89
+ return tokens.map((token) => {
90
+ if (
91
+ (token.startsWith("\"") && token.endsWith("\"")) ||
92
+ (token.startsWith("'") && token.endsWith("'"))
93
+ ) {
94
+ return token.slice(1, -1)
95
+ }
96
+
97
+ return token
98
+ })
99
+ }
100
+
101
+ const CONTROL_TOKENS = new Set(["&&", "||", ";", "|"])
102
+
103
+ function resolveExecutionDirectory(rawCwd: unknown, fallbackDirectory: string): string {
104
+ if (typeof rawCwd !== "string" || rawCwd.trim() === "") {
105
+ return fallbackDirectory
106
+ }
107
+
108
+ return path.isAbsolute(rawCwd) ? rawCwd : path.resolve(fallbackDirectory, rawCwd)
109
+ }
110
+
111
+ function resolveTrackedDeleteTargets(command: string, executionDirectory: string): string[] {
112
+ const tokens = tokenizeCommand(command.trim())
113
+ if (tokens.length === 0) {
114
+ return []
115
+ }
116
+
117
+ const resolvedTargets: string[] = []
118
+ let currentDirectory = executionDirectory
119
+ let index = 0
120
+ let atCommandStart = true
121
+
122
+ while (index < tokens.length) {
123
+ const token = tokens[index]
124
+
125
+ if (CONTROL_TOKENS.has(token)) {
126
+ atCommandStart = true
127
+ index += 1
128
+ continue
129
+ }
130
+
131
+ if (!atCommandStart) {
132
+ index += 1
133
+ continue
134
+ }
135
+
136
+ if (token === "cd") {
137
+ const destination = tokens[index + 1]
138
+ if (destination && !CONTROL_TOKENS.has(destination)) {
139
+ currentDirectory = path.isAbsolute(destination)
140
+ ? path.resolve(destination)
141
+ : path.resolve(currentDirectory, destination)
142
+ index += 2
143
+ } else {
144
+ index += 1
145
+ }
146
+ atCommandStart = false
147
+ continue
148
+ }
149
+
150
+ if (path.basename(token) === "rm") {
151
+ index += 1
152
+ while (index < tokens.length && !CONTROL_TOKENS.has(tokens[index])) {
153
+ const candidate = tokens[index]
154
+ if (candidate === "--" || candidate.startsWith("-")) {
155
+ index += 1
156
+ continue
157
+ }
158
+
159
+ const resolvedCandidate = path.isAbsolute(candidate)
160
+ ? path.resolve(candidate)
161
+ : path.resolve(currentDirectory, candidate)
162
+ if (fs.existsSync(resolvedCandidate) && fs.statSync(resolvedCandidate).isDirectory()) {
163
+ index += 1
164
+ continue
165
+ }
166
+ resolvedTargets.push(resolvedCandidate)
167
+ index += 1
168
+ }
169
+ atCommandStart = false
170
+ continue
171
+ }
172
+
173
+ atCommandStart = false
174
+ index += 1
175
+ }
176
+
177
+ return Array.from(new Set(resolvedTargets))
178
+ }
179
+
65
180
  function normalizeRelativePath(rawPath: string, workspaceDir: string): string {
66
181
  const trimmed = rawPath.trim()
67
182
  if (!trimmed) {
@@ -80,6 +195,14 @@ function normalizeRelativePath(rawPath: string, workspaceDir: string): string {
80
195
  return relativePath.split(path.sep).join("/")
81
196
  }
82
197
 
198
+ function tryNormalizeRelativePath(rawPath: string, workspaceDir: string): string | null {
199
+ try {
200
+ return normalizeRelativePath(rawPath, workspaceDir)
201
+ } catch {
202
+ return null
203
+ }
204
+ }
205
+
83
206
  async function readErrorMessageFromResponse(
84
207
  response: Response,
85
208
  fallbackMessage: string
@@ -104,6 +227,51 @@ async function readErrorMessageFromResponse(
104
227
  }
105
228
  }
106
229
 
230
+ function collectTrackedPathsForTool(
231
+ tool: string,
232
+ inputArgs: ToolArgs,
233
+ outputArgs: ToolArgs,
234
+ workspaceDir: string
235
+ ): string[] {
236
+ if (tool === "apply_patch") {
237
+ const patchPayload = findPatchPayload(outputArgs) ?? findPatchPayload(inputArgs)
238
+ if (!patchPayload) {
239
+ return []
240
+ }
241
+
242
+ return extractTrackedPathsFromPatch(patchPayload)
243
+ }
244
+
245
+ if (tool === "write") {
246
+ const filepath =
247
+ findNestedStringByKeys(outputArgs, new Set(["filepath", "filePath"])) ??
248
+ findNestedStringByKeys(inputArgs, new Set(["filepath", "filePath"]))
249
+ if (!filepath) {
250
+ return []
251
+ }
252
+
253
+ return [filepath]
254
+ }
255
+
256
+ if (tool === "bash") {
257
+ const command =
258
+ findNestedStringByKeys(outputArgs, new Set(["command", "cmd", "script", "arguments"])) ??
259
+ findNestedStringByKeys(inputArgs, new Set(["command", "cmd", "script", "arguments"]))
260
+ if (!command) {
261
+ return []
262
+ }
263
+
264
+ const rawCwd =
265
+ (outputArgs && (outputArgs.workdir ?? outputArgs.cwd)) ??
266
+ (inputArgs && (inputArgs.workdir ?? inputArgs.cwd))
267
+ const executionDirectory = resolveExecutionDirectory(rawCwd, workspaceDir)
268
+
269
+ return resolveTrackedDeleteTargets(command, executionDirectory)
270
+ }
271
+
272
+ return []
273
+ }
274
+
107
275
  export const ApplyPatchSessionDiffPlugin: Plugin = async ({ client, directory }) => {
108
276
  async function resolveRootSessionID(sessionID: string): Promise<string> {
109
277
  let currentSessionID = sessionID
@@ -150,24 +318,22 @@ export const ApplyPatchSessionDiffPlugin: Plugin = async ({ client, directory })
150
318
 
151
319
  return {
152
320
  "tool.execute.before": async (input, output) => {
153
- if (input.tool !== "apply_patch") {
321
+ if (!["apply_patch", "write", "bash"].includes(input.tool)) {
154
322
  return
155
323
  }
156
324
 
157
325
  const rawSessionID = typeof input.sessionID === "string" ? input.sessionID.trim() : ""
158
326
  if (!rawSessionID) {
159
- throw new Error("apply_patch hook could not determine session ID.")
327
+ return
160
328
  }
161
329
 
162
330
  const rootSessionID = await resolveRootSessionID(rawSessionID)
163
- const patchPayload = findPatchPayload(output.args) ?? findPatchPayload(input.args)
164
- if (!patchPayload) {
165
- throw new Error("apply_patch hook could not find patch payload.")
166
- }
167
-
168
- const paths = extractTrackedPathsFromPatch(patchPayload)
331
+ const paths = collectTrackedPathsForTool(input.tool, input.args, output.args, directory)
169
332
  for (const candidatePath of paths) {
170
- const relativePath = normalizeRelativePath(candidatePath, directory)
333
+ const relativePath = tryNormalizeRelativePath(candidatePath, directory)
334
+ if (!relativePath) {
335
+ continue
336
+ }
171
337
  await captureBaselineForPath(rootSessionID, relativePath)
172
338
  }
173
339
  },
@@ -4,4 +4,4 @@
4
4
  },
5
5
  "devDependencies": {},
6
6
  "scripts": {}
7
- }
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teamcopilot",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "A shared AI Agent for Teams",
5
5
  "homepage": "https://teamcopilot.ai",
6
6
  "repository": {