@jackwener/opencli 1.7.5 → 1.7.7

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 (121) hide show
  1. package/README.md +22 -10
  2. package/README.zh-CN.md +18 -9
  3. package/cli-manifest.json +401 -11
  4. package/clis/51job/company.js +125 -0
  5. package/clis/51job/detail.js +108 -0
  6. package/clis/51job/hot.js +55 -0
  7. package/clis/51job/search.js +79 -0
  8. package/clis/51job/utils.js +302 -0
  9. package/clis/51job/utils.test.js +69 -0
  10. package/clis/bilibili/video.js +68 -0
  11. package/clis/bilibili/video.test.js +132 -0
  12. package/clis/chatgpt/image.js +1 -1
  13. package/clis/deepseek/ask.js +37 -11
  14. package/clis/deepseek/ask.test.js +165 -0
  15. package/clis/deepseek/utils.js +192 -24
  16. package/clis/deepseek/utils.test.js +145 -0
  17. package/clis/gemini/image.js +1 -1
  18. package/clis/instagram/download.js +1 -1
  19. package/clis/jianyu/search.js +139 -3
  20. package/clis/jianyu/search.test.js +25 -0
  21. package/clis/jianyu/shared/procurement-detail.js +15 -0
  22. package/clis/jianyu/shared/procurement-detail.test.js +12 -0
  23. package/clis/twitter/likes.js +3 -2
  24. package/clis/twitter/search.js +4 -2
  25. package/clis/twitter/search.test.js +4 -0
  26. package/clis/twitter/shared.js +35 -2
  27. package/clis/twitter/shared.test.js +96 -0
  28. package/clis/twitter/thread.js +3 -1
  29. package/clis/twitter/timeline.js +3 -2
  30. package/clis/twitter/tweets.js +219 -0
  31. package/clis/twitter/tweets.test.js +125 -0
  32. package/clis/web/read.js +25 -5
  33. package/clis/web/read.test.js +76 -0
  34. package/clis/weread/ai-outline.js +170 -0
  35. package/clis/weread/ai-outline.test.js +83 -0
  36. package/clis/weread/book.js +57 -44
  37. package/clis/weread/commands.test.js +24 -0
  38. package/clis/xiaoyuzhou/podcast-episodes.js +2 -2
  39. package/clis/xiaoyuzhou/podcast-episodes.test.js +78 -0
  40. package/clis/youtube/channel.js +35 -0
  41. package/dist/src/browser/analyze.d.ts +103 -0
  42. package/dist/src/browser/analyze.js +230 -0
  43. package/dist/src/browser/analyze.test.d.ts +1 -0
  44. package/dist/src/browser/analyze.test.js +164 -0
  45. package/dist/src/browser/article-extract.d.ts +57 -0
  46. package/dist/src/browser/article-extract.e2e.test.d.ts +1 -0
  47. package/dist/src/browser/article-extract.e2e.test.js +105 -0
  48. package/dist/src/browser/article-extract.js +169 -0
  49. package/dist/src/browser/article-extract.test.d.ts +1 -0
  50. package/dist/src/browser/article-extract.test.js +94 -0
  51. package/dist/src/browser/base-page.d.ts +13 -3
  52. package/dist/src/browser/base-page.js +35 -25
  53. package/dist/src/browser/cdp.d.ts +1 -0
  54. package/dist/src/browser/cdp.js +23 -5
  55. package/dist/src/browser/compound.d.ts +59 -0
  56. package/dist/src/browser/compound.js +112 -0
  57. package/dist/src/browser/compound.test.d.ts +1 -0
  58. package/dist/src/browser/compound.test.js +175 -0
  59. package/dist/src/browser/dom-snapshot.d.ts +7 -0
  60. package/dist/src/browser/dom-snapshot.js +76 -3
  61. package/dist/src/browser/dom-snapshot.test.js +65 -0
  62. package/dist/src/browser/extract.d.ts +69 -0
  63. package/dist/src/browser/extract.js +132 -0
  64. package/dist/src/browser/extract.test.d.ts +1 -0
  65. package/dist/src/browser/extract.test.js +129 -0
  66. package/dist/src/browser/find.d.ts +76 -0
  67. package/dist/src/browser/find.js +179 -0
  68. package/dist/src/browser/find.test.d.ts +1 -0
  69. package/dist/src/browser/find.test.js +120 -0
  70. package/dist/src/browser/html-tree.d.ts +75 -0
  71. package/dist/src/browser/html-tree.js +112 -0
  72. package/dist/src/browser/html-tree.test.d.ts +1 -0
  73. package/dist/src/browser/html-tree.test.js +181 -0
  74. package/dist/src/browser/network-cache.d.ts +48 -0
  75. package/dist/src/browser/network-cache.js +66 -0
  76. package/dist/src/browser/network-cache.test.d.ts +1 -0
  77. package/dist/src/browser/network-cache.test.js +58 -0
  78. package/dist/src/browser/network-key.d.ts +22 -0
  79. package/dist/src/browser/network-key.js +66 -0
  80. package/dist/src/browser/network-key.test.d.ts +1 -0
  81. package/dist/src/browser/network-key.test.js +49 -0
  82. package/dist/src/browser/shape-filter.d.ts +52 -0
  83. package/dist/src/browser/shape-filter.js +101 -0
  84. package/dist/src/browser/shape-filter.test.d.ts +1 -0
  85. package/dist/src/browser/shape-filter.test.js +101 -0
  86. package/dist/src/browser/shape.d.ts +23 -0
  87. package/dist/src/browser/shape.js +95 -0
  88. package/dist/src/browser/shape.test.d.ts +1 -0
  89. package/dist/src/browser/shape.test.js +82 -0
  90. package/dist/src/browser/target-errors.d.ts +14 -1
  91. package/dist/src/browser/target-errors.js +13 -0
  92. package/dist/src/browser/target-errors.test.js +39 -6
  93. package/dist/src/browser/target-resolver.d.ts +57 -10
  94. package/dist/src/browser/target-resolver.js +195 -75
  95. package/dist/src/browser/target-resolver.test.js +80 -5
  96. package/dist/src/browser/verify-fixture.d.ts +59 -0
  97. package/dist/src/browser/verify-fixture.js +213 -0
  98. package/dist/src/browser/verify-fixture.test.d.ts +1 -0
  99. package/dist/src/browser/verify-fixture.test.js +161 -0
  100. package/dist/src/cli.d.ts +32 -0
  101. package/dist/src/cli.js +936 -141
  102. package/dist/src/cli.test.js +1051 -1
  103. package/dist/src/daemon.d.ts +3 -2
  104. package/dist/src/daemon.js +16 -4
  105. package/dist/src/daemon.test.d.ts +1 -0
  106. package/dist/src/daemon.test.js +19 -0
  107. package/dist/src/download/article-download.d.ts +12 -0
  108. package/dist/src/download/article-download.js +141 -17
  109. package/dist/src/download/article-download.test.js +196 -0
  110. package/dist/src/download/index.js +73 -86
  111. package/dist/src/errors.js +4 -2
  112. package/dist/src/errors.test.js +13 -0
  113. package/dist/src/execution.js +7 -2
  114. package/dist/src/execution.test.js +54 -0
  115. package/dist/src/launcher.d.ts +1 -1
  116. package/dist/src/launcher.js +3 -3
  117. package/dist/src/main.js +16 -0
  118. package/dist/src/output.js +1 -1
  119. package/dist/src/output.test.js +6 -0
  120. package/dist/src/types.d.ts +18 -3
  121. package/package.json +5 -1
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Verify fixture: structural expectations for `opencli browser verify` output.
3
+ *
4
+ * The adapter-author skill runbook says every published adapter must write a
5
+ * fixture under `~/.opencli/sites/<site>/verify/<command>.json` so later verify
6
+ * runs can catch shape regressions (missing columns, wrong types, bleeding
7
+ * values) without relying on exact content match — BBS / news / market data is
8
+ * too volatile for value equality.
9
+ *
10
+ * Schema:
11
+ * {
12
+ * // args can be either:
13
+ * // - an object of named flags: { "limit": 3 } → expands to `--limit 3`
14
+ * // - a raw argv array: ["123", "--limit", "3"] → passed verbatim
15
+ * // Use the array form for adapters that take positional subjects (e.g. <tid>, <url>, <query>).
16
+ * "args": { "limit": 3 },
17
+ * "expect": {
18
+ * "rowCount": { "min": 1, "max": 10 }, // inclusive bounds
19
+ * "columns": ["a", "b"], // every row must have these keys
20
+ * "types": { "a": "string", "b": "number|string" },
21
+ * "patterns": { "url": "^https?://" },
22
+ * "notEmpty": ["title", "url"], // trimmed string must be non-empty
23
+ * "mustNotContain": { // catch content-contamination bleed
24
+ * "description": ["address:", "category:"]
25
+ * },
26
+ * "mustBeTruthy": ["count"] // catch silent `|| 0` fallbacks
27
+ * }
28
+ * }
29
+ */
30
+ import * as fs from 'node:fs';
31
+ import * as os from 'node:os';
32
+ import * as path from 'node:path';
33
+ export function fixturePath(site, command) {
34
+ return path.join(os.homedir(), '.opencli', 'sites', site, 'verify', `${command}.json`);
35
+ }
36
+ export function loadFixture(site, command) {
37
+ const p = fixturePath(site, command);
38
+ if (!fs.existsSync(p))
39
+ return null;
40
+ try {
41
+ const raw = fs.readFileSync(p, 'utf-8');
42
+ const parsed = JSON.parse(raw);
43
+ return parsed;
44
+ }
45
+ catch (err) {
46
+ throw new Error(`Failed to parse fixture ${p}: ${err instanceof Error ? err.message : String(err)}`);
47
+ }
48
+ }
49
+ export function writeFixture(site, command, fixture) {
50
+ const p = fixturePath(site, command);
51
+ fs.mkdirSync(path.dirname(p), { recursive: true });
52
+ fs.writeFileSync(p, `${JSON.stringify(fixture, null, 2)}\n`, 'utf-8');
53
+ return p;
54
+ }
55
+ /**
56
+ * Derive a reasonable fixture from sample output. Used by `--write-fixture`
57
+ * to seed a first draft the author can hand-tune.
58
+ *
59
+ * Heuristics:
60
+ * - rowCount.min = 1 if rows non-empty, else 0
61
+ * - columns = keys from the first row
62
+ * - types = typeof of the first row's values, with "number|string" for mixed
63
+ * - no auto patterns / notEmpty — author should add those deliberately
64
+ */
65
+ export function deriveFixture(rows, args) {
66
+ const expect = {};
67
+ if (rows.length === 0) {
68
+ expect.rowCount = { min: 0 };
69
+ return { ...(args ? { args } : {}), expect };
70
+ }
71
+ expect.rowCount = { min: 1 };
72
+ const first = rows[0];
73
+ const columns = Object.keys(first);
74
+ expect.columns = columns;
75
+ const types = {};
76
+ for (const col of columns) {
77
+ const observed = new Set();
78
+ for (const row of rows) {
79
+ const v = row[col];
80
+ observed.add(jsType(v));
81
+ }
82
+ types[col] = [...observed].sort().join('|');
83
+ }
84
+ expect.types = types;
85
+ return { ...(args ? { args } : {}), expect };
86
+ }
87
+ export function validateRows(rows, fixture) {
88
+ const failures = [];
89
+ const expect = fixture.expect;
90
+ if (!expect)
91
+ return failures;
92
+ if (expect.rowCount) {
93
+ const { min, max } = expect.rowCount;
94
+ if (typeof min === 'number' && rows.length < min) {
95
+ failures.push({ rule: 'rowCount', detail: `got ${rows.length} rows, expected at least ${min}` });
96
+ }
97
+ if (typeof max === 'number' && rows.length > max) {
98
+ failures.push({ rule: 'rowCount', detail: `got ${rows.length} rows, expected at most ${max}` });
99
+ }
100
+ }
101
+ const columns = expect.columns ?? [];
102
+ const types = expect.types ?? {};
103
+ const patterns = expect.patterns ?? {};
104
+ const notEmpty = expect.notEmpty ?? [];
105
+ const compiledPatterns = {};
106
+ for (const [col, src] of Object.entries(patterns)) {
107
+ try {
108
+ compiledPatterns[col] = new RegExp(src);
109
+ }
110
+ catch (err) {
111
+ failures.push({ rule: 'pattern', detail: `pattern for "${col}" invalid: ${err instanceof Error ? err.message : String(err)}` });
112
+ }
113
+ }
114
+ rows.forEach((row, i) => {
115
+ for (const col of columns) {
116
+ if (!(col in row)) {
117
+ failures.push({ rule: 'column', detail: `missing column "${col}"`, rowIndex: i });
118
+ }
119
+ }
120
+ for (const [col, declared] of Object.entries(types)) {
121
+ if (!(col in row))
122
+ continue;
123
+ const actual = jsType(row[col]);
124
+ if (!typeMatches(actual, declared)) {
125
+ failures.push({
126
+ rule: 'type',
127
+ detail: `"${col}" is ${actual}, expected ${declared}`,
128
+ rowIndex: i,
129
+ });
130
+ }
131
+ }
132
+ for (const [col, re] of Object.entries(compiledPatterns)) {
133
+ if (!(col in row))
134
+ continue;
135
+ const v = row[col];
136
+ if (v === null || v === undefined)
137
+ continue;
138
+ if (!re.test(String(v))) {
139
+ failures.push({
140
+ rule: 'pattern',
141
+ detail: `"${col}"=${JSON.stringify(String(v).slice(0, 60))} does not match /${re.source}/`,
142
+ rowIndex: i,
143
+ });
144
+ }
145
+ }
146
+ for (const col of notEmpty) {
147
+ const v = row[col];
148
+ if (v === null || v === undefined || String(v).trim() === '') {
149
+ failures.push({ rule: 'notEmpty', detail: `"${col}" is empty`, rowIndex: i });
150
+ }
151
+ }
152
+ for (const [col, needles] of Object.entries(expect.mustNotContain ?? {})) {
153
+ if (!(col in row))
154
+ continue;
155
+ const v = row[col];
156
+ if (v === null || v === undefined)
157
+ continue;
158
+ const haystack = String(v);
159
+ for (const needle of needles) {
160
+ if (haystack.includes(needle)) {
161
+ failures.push({
162
+ rule: 'mustNotContain',
163
+ detail: `"${col}" contains forbidden substring ${JSON.stringify(needle)}`,
164
+ rowIndex: i,
165
+ });
166
+ }
167
+ }
168
+ }
169
+ for (const col of expect.mustBeTruthy ?? []) {
170
+ if (!(col in row))
171
+ continue;
172
+ if (!row[col]) {
173
+ failures.push({
174
+ rule: 'mustBeTruthy',
175
+ detail: `"${col}" is falsy (${JSON.stringify(row[col])}) — likely silent fallback`,
176
+ rowIndex: i,
177
+ });
178
+ }
179
+ }
180
+ });
181
+ return failures;
182
+ }
183
+ /**
184
+ * Convert fixture args into argv tokens appended after the command name.
185
+ * - Array form is passed through verbatim (stringified), supporting positional subjects.
186
+ * - Object form is expanded to `--key value` pairs.
187
+ */
188
+ export function expandFixtureArgs(args) {
189
+ if (!args)
190
+ return [];
191
+ if (Array.isArray(args))
192
+ return args.map((v) => String(v));
193
+ const out = [];
194
+ for (const [k, v] of Object.entries(args)) {
195
+ out.push(`--${k}`, String(v));
196
+ }
197
+ return out;
198
+ }
199
+ function jsType(v) {
200
+ if (v === null)
201
+ return 'null';
202
+ if (Array.isArray(v))
203
+ return 'array';
204
+ return typeof v;
205
+ }
206
+ function typeMatches(actual, declared) {
207
+ const allowed = declared.split('|').map((s) => s.trim()).filter(Boolean);
208
+ if (allowed.length === 0)
209
+ return true;
210
+ if (allowed.includes('any'))
211
+ return true;
212
+ return allowed.includes(actual);
213
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,161 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { deriveFixture, expandFixtureArgs, validateRows } from './verify-fixture.js';
3
+ describe('validateRows', () => {
4
+ it('passes when rows meet all expectations', () => {
5
+ const fixture = {
6
+ expect: {
7
+ rowCount: { min: 1, max: 3 },
8
+ columns: ['id', 'title', 'url'],
9
+ types: { id: 'number', title: 'string', url: 'string' },
10
+ patterns: { url: '^https://' },
11
+ notEmpty: ['title', 'url'],
12
+ },
13
+ };
14
+ const rows = [
15
+ { id: 1, title: 'a', url: 'https://x.com/a' },
16
+ { id: 2, title: 'b', url: 'https://x.com/b' },
17
+ ];
18
+ expect(validateRows(rows, fixture)).toEqual([]);
19
+ });
20
+ it('reports rowCount below min', () => {
21
+ const failures = validateRows([], { expect: { rowCount: { min: 1 } } });
22
+ expect(failures).toHaveLength(1);
23
+ expect(failures[0]).toMatchObject({ rule: 'rowCount' });
24
+ expect(failures[0].detail).toContain('at least 1');
25
+ });
26
+ it('reports rowCount above max', () => {
27
+ const failures = validateRows([{}, {}, {}, {}], { expect: { rowCount: { max: 3 } } });
28
+ expect(failures).toHaveLength(1);
29
+ expect(failures[0].detail).toContain('at most 3');
30
+ });
31
+ it('reports missing columns per row', () => {
32
+ const failures = validateRows([{ a: 1 }, { a: 2, b: 3 }], { expect: { columns: ['a', 'b'] } });
33
+ // row 0 missing 'b', row 1 complete
34
+ expect(failures).toEqual([
35
+ { rule: 'column', detail: 'missing column "b"', rowIndex: 0 },
36
+ ]);
37
+ });
38
+ it('reports type mismatch including null', () => {
39
+ const failures = validateRows([{ a: 'abc' }, { a: null }, { a: 42 }], { expect: { types: { a: 'string' } } });
40
+ // row 0 string ok, row 1 null fail, row 2 number fail
41
+ expect(failures).toHaveLength(2);
42
+ expect(failures[0].rowIndex).toBe(1);
43
+ expect(failures[0].detail).toContain('null');
44
+ expect(failures[1].rowIndex).toBe(2);
45
+ expect(failures[1].detail).toContain('number');
46
+ });
47
+ it('accepts union types like "number|string"', () => {
48
+ const failures = validateRows([{ id: 1 }, { id: 'abc' }], { expect: { types: { id: 'number|string' } } });
49
+ expect(failures).toEqual([]);
50
+ });
51
+ it('accepts "any" as wildcard type', () => {
52
+ const failures = validateRows([{ v: 1 }, { v: 'x' }, { v: null }, { v: [1, 2] }], { expect: { types: { v: 'any' } } });
53
+ expect(failures).toEqual([]);
54
+ });
55
+ it('reports pattern mismatch with row index and truncated value', () => {
56
+ const failures = validateRows([{ url: 'https://ok.com' }, { url: 'not-a-url' }], { expect: { patterns: { url: '^https?://' } } });
57
+ expect(failures).toHaveLength(1);
58
+ expect(failures[0]).toMatchObject({ rule: 'pattern', rowIndex: 1 });
59
+ expect(failures[0].detail).toContain('not-a-url');
60
+ });
61
+ it('skips pattern check for null/undefined values', () => {
62
+ const failures = validateRows([{ url: null }, { url: undefined }], { expect: { patterns: { url: '^x' } } });
63
+ expect(failures).toEqual([]);
64
+ });
65
+ it('reports invalid regex without crashing', () => {
66
+ const failures = validateRows([{ a: 'x' }], { expect: { patterns: { a: '[unclosed' } } });
67
+ expect(failures.some((f) => f.rule === 'pattern' && f.detail.includes('invalid'))).toBe(true);
68
+ });
69
+ it('treats empty/whitespace/null as failing notEmpty', () => {
70
+ const failures = validateRows([{ t: '' }, { t: ' ' }, { t: null }, { t: 'ok' }], { expect: { notEmpty: ['t'] } });
71
+ expect(failures).toHaveLength(3);
72
+ expect(failures.map((f) => f.rowIndex)).toEqual([0, 1, 2]);
73
+ });
74
+ it('no failures when fixture has no expect block', () => {
75
+ expect(validateRows([{ anything: 1 }], {})).toEqual([]);
76
+ });
77
+ it('mustNotContain flags substring bleed in columns', () => {
78
+ const failures = validateRows([
79
+ { description: 'Lead engineer, 5 years exp. address: Shanghai. category: IT' },
80
+ { description: 'Clean text.' },
81
+ ], {
82
+ expect: {
83
+ mustNotContain: { description: ['address:', 'category:'] },
84
+ },
85
+ });
86
+ expect(failures).toHaveLength(2);
87
+ expect(failures.every((f) => f.rule === 'mustNotContain')).toBe(true);
88
+ expect(failures.every((f) => f.rowIndex === 0)).toBe(true);
89
+ });
90
+ it('mustNotContain skips null/undefined values', () => {
91
+ const failures = validateRows([{ description: null }, { description: undefined }], { expect: { mustNotContain: { description: ['x'] } } });
92
+ expect(failures).toEqual([]);
93
+ });
94
+ it('mustBeTruthy catches silent 0 / false / "" fallbacks', () => {
95
+ const failures = validateRows([{ count: 10 }, { count: 0 }, { count: false }, { count: '' }, { count: null }], { expect: { mustBeTruthy: ['count'] } });
96
+ expect(failures).toHaveLength(4);
97
+ expect(failures.every((f) => f.rule === 'mustBeTruthy')).toBe(true);
98
+ expect(failures.map((f) => f.rowIndex)).toEqual([1, 2, 3, 4]);
99
+ });
100
+ });
101
+ describe('deriveFixture', () => {
102
+ it('returns rowCount.min=0 when rows are empty', () => {
103
+ expect(deriveFixture([])).toEqual({ expect: { rowCount: { min: 0 } } });
104
+ });
105
+ it('extracts columns from first row and infers types per column', () => {
106
+ const fixture = deriveFixture([
107
+ { id: 1, title: 'a', url: 'https://x' },
108
+ { id: 2, title: 'b', url: 'https://y' },
109
+ ]);
110
+ expect(fixture.expect?.columns).toEqual(['id', 'title', 'url']);
111
+ expect(fixture.expect?.types).toEqual({
112
+ id: 'number',
113
+ title: 'string',
114
+ url: 'string',
115
+ });
116
+ expect(fixture.expect?.rowCount).toEqual({ min: 1 });
117
+ });
118
+ it('unions mixed types across rows as "a|b"', () => {
119
+ const fixture = deriveFixture([
120
+ { v: 1 },
121
+ { v: 'two' },
122
+ { v: null },
123
+ ]);
124
+ expect(fixture.expect?.types?.v).toBe('null|number|string');
125
+ });
126
+ it('embeds args when provided', () => {
127
+ const fixture = deriveFixture([{ x: 1 }], { limit: 5 });
128
+ expect(fixture.args).toEqual({ limit: 5 });
129
+ });
130
+ it('embeds positional argv array when provided', () => {
131
+ const fixture = deriveFixture([{ x: 1 }], ['123', '--limit', '3']);
132
+ expect(fixture.args).toEqual(['123', '--limit', '3']);
133
+ });
134
+ it('does not add patterns or notEmpty automatically', () => {
135
+ const fixture = deriveFixture([{ a: 'x' }]);
136
+ expect(fixture.expect?.patterns).toBeUndefined();
137
+ expect(fixture.expect?.notEmpty).toBeUndefined();
138
+ });
139
+ });
140
+ describe('expandFixtureArgs', () => {
141
+ it('returns [] for undefined', () => {
142
+ expect(expandFixtureArgs(undefined)).toEqual([]);
143
+ });
144
+ it('expands object form as --key value pairs', () => {
145
+ expect(expandFixtureArgs({ limit: 3, sort: 'hot' })).toEqual(['--limit', '3', '--sort', 'hot']);
146
+ });
147
+ it('passes array form verbatim, stringifying values', () => {
148
+ expect(expandFixtureArgs(['123456', '--limit', 3])).toEqual(['123456', '--limit', '3']);
149
+ });
150
+ it('handles empty object and empty array', () => {
151
+ expect(expandFixtureArgs({})).toEqual([]);
152
+ expect(expandFixtureArgs([])).toEqual([]);
153
+ });
154
+ it('preserves positional + flag mix (e.g. <tid> --limit 3)', () => {
155
+ expect(expandFixtureArgs(['https://example.com/thread-1', '--comments', '5'])).toEqual([
156
+ 'https://example.com/thread-1',
157
+ '--comments',
158
+ '5',
159
+ ]);
160
+ });
161
+ });
package/dist/src/cli.d.ts CHANGED
@@ -6,6 +6,38 @@
6
6
  */
7
7
  import { Command } from 'commander';
8
8
  import { findPackageRoot } from './package-paths.js';
9
+ /**
10
+ * Check whether the site-memory scaffolding exists under
11
+ * ~/.opencli/sites/<site>/. Agents have a strong tendency to forget to write
12
+ * endpoints.json / notes.md after a successful verify, which dooms the next
13
+ * agent to redo recon from scratch. Surfacing the current state as part of
14
+ * verify's final report converts that "silent skip" into a visible nudge;
15
+ * `--strict-memory` escalates it to a failure so agents driving a hardened
16
+ * workflow can't forget.
17
+ */
18
+ export type SiteMemoryReport = {
19
+ ok: boolean;
20
+ siteDir: string;
21
+ endpoints: {
22
+ present: boolean;
23
+ count: number;
24
+ path: string;
25
+ };
26
+ notes: {
27
+ present: boolean;
28
+ path: string;
29
+ };
30
+ };
31
+ export declare function checkSiteMemory(site: string): SiteMemoryReport;
32
+ export declare function printSiteMemoryReport(report: SiteMemoryReport, strict: boolean | undefined): void;
33
+ /** Coerce adapter JSON output into a row array. Accepts `[{...}]`, single `{}`, or `{items:[...]}`-style envelopes. */
34
+ export declare function normalizeVerifyRows(data: unknown): Record<string, unknown>[];
35
+ /** Render up to 10 rows as a compact padded table for eyeball inspection during verify. */
36
+ export declare function renderVerifyPreview(rows: Record<string, unknown>[], opts?: {
37
+ maxRows?: number;
38
+ maxCols?: number;
39
+ cellMax?: number;
40
+ }): string;
9
41
  export declare function createProgram(BUILTIN_CLIS: string, USER_CLIS: string): Command;
10
42
  export declare function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void;
11
43
  export interface BrowserVerifyInvocation {