cowork-cli 2.0.0 → 2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cowork-cli",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "work with cowork",
5
5
  "bin": {
6
6
  "cwk": "bin/cli.js"
package/src/main.js CHANGED
@@ -15,8 +15,20 @@ const PKG_PATH = path.join(__dirname, '../package.json');
15
15
  * @param {string[]} args Command line arguments.
16
16
  */
17
17
  export default async function main(args) {
18
+ let pipedData = '';
19
+ if (!process.stdin.isTTY) {
20
+ try {
21
+ for await (const chunk of process.stdin) {
22
+ pipedData += chunk;
23
+ }
24
+ pipedData = pipedData.trim();
25
+ } catch (err) {
26
+ logger.error(`Error reading from stdin: ${err.message}`);
27
+ }
28
+ }
29
+
18
30
  // Handle empty arguments or help flags
19
- if (args.length === 0 || args[0] === '-h' || args[0] === '--help') {
31
+ if ((args.length === 0 && !pipedData) || args[0] === '-h' || args[0] === '--help') {
20
32
  show_help();
21
33
  return;
22
34
  }
@@ -33,7 +45,12 @@ export default async function main(args) {
33
45
  }
34
46
 
35
47
  // Handle query execution
36
- const query = args[0];
48
+ let query = args.join(' ').trim();
49
+ if (pipedData) {
50
+ query = query
51
+ ? `${query}\n\n[Piped Input]:\n${pipedData}`
52
+ : `Analyze the following data:\n\n[Piped Input]:\n${pipedData}`;
53
+ }
37
54
  const config = loadConfig();
38
55
 
39
56
  // clientLoader handles config validation and throws if invalid
@@ -207,31 +207,51 @@ export function clearIgnoreCache() {
207
207
  * and the **last matching pattern wins**. Negation patterns (`!`) can
208
208
  * therefore un-ignore a previously ignored name.
209
209
  *
210
- * @param {string} name Item basename.
211
- * @param {Object[]} ignoreList Structured pattern objects.
210
+ * @param {string} name Item basename.
211
+ * @param {Object[]} ignoreList Structured pattern objects.
212
212
  * @param {Object} [options]
213
- * @param {boolean} [options.isDirectory] Whether the item is a directory.
213
+ * @param {boolean} [options.isDirectory] Whether the item is a directory.
214
214
  * When omitted, directory-only patterns match regardless
215
215
  * (backward-compatible with existing callers).
216
+ * @param {string} [options.relativePath] Path relative to the project root
217
+ * (e.g. `"src/internal/foo.js"`). When provided, patterns
218
+ * that contain a `/` are tested against this value instead
219
+ * of being skipped. When omitted the old skip behaviour is
220
+ * preserved for backward compatibility.
216
221
  * @returns {boolean} `true` if the item should be skipped.
217
222
  */
218
223
  export function shouldIgnore(name, ignoreList, options = {}) {
219
- const { isDirectory } = options;
224
+ const { isDirectory, relativePath } = options;
220
225
  let ignored = false;
221
226
 
222
227
  for (const entry of ignoreList) {
223
- // Path-containing patterns (e.g. `docs/internal`) require full
224
- // relative-path matching which callers don't supply — skip them.
225
- if (entry.hasSlash) continue;
226
-
227
228
  // Directory-only patterns (`build/`) don't apply to files.
228
229
  // When `isDirectory` is undefined the caller didn't say, so we match
229
230
  // to preserve backward compat with callers that only pass basenames.
230
231
  if (entry.dirOnly && isDirectory === false) continue;
231
232
 
232
- const matches = entry.hasGlob && entry.regex
233
- ? entry.regex.test(name)
234
- : name === entry.pattern;
233
+ let matches = false;
234
+
235
+ if (entry.hasSlash) {
236
+ // Path-scoped pattern — needs a full relative path to evaluate.
237
+ // Without one we conservatively skip it (backward-compatible).
238
+ if (!relativePath) continue;
239
+
240
+ if (entry.hasGlob && entry.regex) {
241
+ matches = entry.regex.test(relativePath);
242
+ } else {
243
+ // Exact segment match: the pattern must equal the relative path
244
+ // OR appear as a leading path prefix followed by a separator.
245
+ matches =
246
+ relativePath === entry.pattern ||
247
+ relativePath.startsWith(entry.pattern + '/');
248
+ }
249
+ } else {
250
+ // Basename-only pattern — match against the entry name.
251
+ matches = entry.hasGlob && entry.regex
252
+ ? entry.regex.test(name)
253
+ : name === entry.pattern;
254
+ }
235
255
 
236
256
  if (matches) {
237
257
  ignored = !entry.negated;
@@ -263,7 +283,7 @@ export function safePath(inputPath) {
263
283
  *
264
284
  * Rejects:
265
285
  * 1. Symbolic links (could escape the project sandbox).
266
- * 2. Names matched by the ignore list.
286
+ * 2. Names matched by the ignore list (basename AND path-scoped patterns).
267
287
  * 3. Paths that resolve outside `process.cwd()`.
268
288
  *
269
289
  * @param {import('fs').Dirent} dirent Directory entry from `readdir`.
@@ -274,12 +294,15 @@ export function safePath(inputPath) {
274
294
  export function isSafeEntry(dirent, parentPath, ignoreList) {
275
295
  if (dirent.isSymbolicLink()) return false;
276
296
 
277
- const isDir = dirent.isDirectory();
278
- if (shouldIgnore(dirent.name, ignoreList, { isDirectory: isDir })) return false;
279
-
280
- const resolved = path.resolve(parentPath, dirent.name);
281
297
  const root = process.cwd();
298
+ const resolved = path.resolve(parentPath, dirent.name);
282
299
  if (resolved !== root && !resolved.startsWith(root + path.sep)) return false;
283
300
 
301
+ const isDir = dirent.isDirectory();
302
+ // Compute the relative path so shouldIgnore() can evaluate path-scoped
303
+ // patterns like `tests/fixtures/` in addition to bare-name patterns.
304
+ const relativePath = path.relative(root, resolved);
305
+ if (shouldIgnore(dirent.name, ignoreList, { isDirectory: isDir, relativePath })) return false;
306
+
284
307
  return true;
285
308
  }