@sabaiway/agent-workflow-kit 1.3.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +70 -1
  2. package/README.md +15 -5
  3. package/SKILL.md +81 -9
  4. package/bin/install.mjs +59 -8
  5. package/bin/install.test.mjs +66 -0
  6. package/capability.json +21 -0
  7. package/migrations/1.1.0-communication-language.md +5 -5
  8. package/migrations/README.md +2 -1
  9. package/package.json +8 -5
  10. package/references/contracts.md +2 -2
  11. package/references/scripts/archive-changelog.mjs +1 -4
  12. package/references/templates/AGENTS.md +2 -2
  13. package/tools/delegation.mjs +109 -0
  14. package/tools/delegation.test.mjs +115 -0
  15. package/tools/detect-backends.mjs +310 -0
  16. package/tools/detect-backends.test.mjs +342 -0
  17. package/tools/inject-methodology.mjs +111 -0
  18. package/tools/inject-methodology.test.mjs +124 -0
  19. package/tools/manifest/fixtures/bad-available/SKILL.md +7 -0
  20. package/tools/manifest/fixtures/bad-available/capability.json +10 -0
  21. package/tools/manifest/fixtures/detect-array/SKILL.md +7 -0
  22. package/tools/manifest/fixtures/detect-array/capability.json +10 -0
  23. package/tools/manifest/fixtures/malformed-json/capability.json +1 -0
  24. package/tools/manifest/fixtures/metadata-version/SKILL.md +10 -0
  25. package/tools/manifest/fixtures/metadata-version/capability.json +9 -0
  26. package/tools/manifest/fixtures/missing-key/SKILL.md +7 -0
  27. package/tools/manifest/fixtures/missing-key/capability.json +8 -0
  28. package/tools/manifest/fixtures/missing-source/SKILL.md +7 -0
  29. package/tools/manifest/fixtures/missing-source/capability.json +11 -0
  30. package/tools/manifest/fixtures/nested-version-decoy/SKILL.md +10 -0
  31. package/tools/manifest/fixtures/nested-version-decoy/capability.json +9 -0
  32. package/tools/manifest/fixtures/null-root/capability.json +1 -0
  33. package/tools/manifest/fixtures/provides-roles-mismatch/SKILL.md +7 -0
  34. package/tools/manifest/fixtures/provides-roles-mismatch/bin/run.sh +2 -0
  35. package/tools/manifest/fixtures/provides-roles-mismatch/capability.json +11 -0
  36. package/tools/manifest/fixtures/stub/capability.json +10 -0
  37. package/tools/manifest/fixtures/traversal-source/SKILL.md +7 -0
  38. package/tools/manifest/fixtures/traversal-source/capability.json +11 -0
  39. package/tools/manifest/fixtures/unknown-schema/capability.json +9 -0
  40. package/tools/manifest/fixtures/valid/SKILL.md +10 -0
  41. package/tools/manifest/fixtures/valid/bin/run.sh +3 -0
  42. package/tools/manifest/fixtures/valid/capability.json +18 -0
  43. package/tools/manifest/fixtures/version-mismatch/SKILL.md +7 -0
  44. package/tools/manifest/fixtures/version-mismatch/capability.json +9 -0
  45. package/tools/manifest/fixtures/win-absolute-source/SKILL.md +7 -0
  46. package/tools/manifest/fixtures/win-absolute-source/capability.json +11 -0
  47. package/tools/manifest/schema.md +67 -0
  48. package/tools/manifest/validate.mjs +264 -0
  49. package/tools/manifest/validate.test.mjs +73 -0
  50. package/tools/methodology-slot.md +1 -0
  51. package/tools/release-scan.mjs +103 -0
  52. package/tools/release-scan.test.mjs +41 -0
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env node
2
+ // Release-diff scanner — a CI/authoring gate that FAILS on AI/reviewer attribution that must never
3
+ // ship in a release:
4
+ // - co-author trailers, "Generated with <AI>" footers, "Reviewed by <AI>" / authorship metadata.
5
+ //
6
+ // Mentioning a model as a *product* (codex, Gemini, GPT-OSS) is fine; only an *attribution*
7
+ // construction is flagged. An ALLOWLIST of legitimate component names keeps real product names from
8
+ // tripping the (narrow) attribution rules. Only the TEST is self-excluded (it holds attribution
9
+ // fixtures); the scanner source contains no attribution construction, so it is scanned normally.
10
+ //
11
+ // Pure `scanText` for unit fixtures; `scanPaths`/CLI for the release tree. Dependency-free, Node >= 18.
12
+
13
+ import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
14
+ import { join, extname, basename, resolve } from 'node:path';
15
+ import { pathToFileURL } from 'node:url';
16
+
17
+ export const ALLOWLIST = [
18
+ 'codex-cli-bridge',
19
+ 'codex-exec',
20
+ 'codex-review',
21
+ 'antigravity-cli-bridge',
22
+ 'agy-run',
23
+ 'agent-workflow-kit',
24
+ 'agent-workflow-memory',
25
+ 'agent-workflow-engine',
26
+ ];
27
+
28
+ // Narrow, high-signal attribution patterns. Mentioning a model as a *product* (codex, Gemini,
29
+ // GPT-OSS) is fine; these match only an *attribution* construction.
30
+ const ATTRIBUTION = [
31
+ { re: /co-?authored-by:/i, label: 'co-author trailer' },
32
+ { re: /🤖\s*generated with/i, label: 'AI "Generated with" footer' },
33
+ { re: /generated with \[?(claude|codex|chatgpt|gpt|gemini|copilot|cursor)\b/i, label: 'AI "Generated with" footer' },
34
+ { re: /reviewed by (claude|codex|chatgpt|gpt|gemini|copilot|cursor|the (ai|model|agent))/i, label: 'AI review attribution' },
35
+ { re: /authored[- ]by[: ][^\n]{0,40}\b(claude|chatgpt|gemini|copilot)\b/i, label: 'AI authorship attribution' },
36
+ ];
37
+
38
+ const allowlistCovers = (matched, allowlist) =>
39
+ allowlist.some((term) => matched.toLowerCase().includes(term.toLowerCase()));
40
+
41
+ // PURE: classify one text blob. Flags AI/reviewer attribution constructions.
42
+ export const scanText = (text, { allowlist = ALLOWLIST } = {}) => {
43
+ const findings = [];
44
+ text.split('\n').forEach((line, idx) => {
45
+ for (const { re, label } of ATTRIBUTION) {
46
+ const m = line.match(re);
47
+ if (m && !allowlistCovers(m[0], allowlist)) {
48
+ findings.push({ line: idx + 1, kind: 'attribution', detail: `${label}: "${m[0]}"` });
49
+ }
50
+ }
51
+ });
52
+ return findings;
53
+ };
54
+
55
+ const TEXT_EXT = new Set(['.md', '.mjs', '.js', '.json', '.sh', '.yml', '.yaml', '.txt', '']);
56
+ const EXCLUDE_DIR_NAMES = new Set(['node_modules', '.git', 'plans', 'scan-fixtures']);
57
+ // Only the TEST is self-excluded — it necessarily contains attribution fixtures. The scanner source
58
+ // carries no attribution construction (its regex literals don't self-match), so it is scanned normally.
59
+ const EXCLUDE_FILE_NAMES = new Set(['release-scan.test.mjs']);
60
+
61
+ const walk = (path, acc = []) => {
62
+ const st = statSync(path);
63
+ if (st.isDirectory()) {
64
+ if (EXCLUDE_DIR_NAMES.has(basename(path))) return acc;
65
+ for (const entry of readdirSync(path)) walk(join(path, entry), acc);
66
+ } else if (st.isFile()) {
67
+ if (EXCLUDE_FILE_NAMES.has(basename(path))) return acc;
68
+ if (TEXT_EXT.has(extname(path)) && !path.endsWith('.tgz')) acc.push(path);
69
+ }
70
+ return acc;
71
+ };
72
+
73
+ export const scanPaths = (paths, opts = {}) => {
74
+ const report = [];
75
+ for (const p of paths) {
76
+ if (!existsSync(p)) continue;
77
+ for (const file of walk(resolve(p))) {
78
+ const findings = scanText(readFileSync(file, 'utf8'), opts);
79
+ for (const f of findings) report.push({ file, ...f });
80
+ }
81
+ }
82
+ return report;
83
+ };
84
+
85
+ const main = (argv) => {
86
+ const reportOnly = argv.includes('--report');
87
+ const paths = argv.filter((a) => a !== '--report');
88
+ if (paths.length === 0) {
89
+ console.error('usage: release-scan.mjs [--report] <path>...');
90
+ process.exit(2);
91
+ }
92
+ const report = scanPaths(paths);
93
+ for (const r of report) console.log(`[${r.kind}] ${r.file}:${r.line} — ${r.detail}`);
94
+ if (report.length === 0) {
95
+ console.log('[release-scan] clean — no AI attribution found.');
96
+ return;
97
+ }
98
+ console.log(`\n[release-scan] ${report.length} finding(s).`);
99
+ if (!reportOnly) process.exit(1);
100
+ };
101
+
102
+ const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
103
+ if (isDirectRun) main(process.argv.slice(2));
@@ -0,0 +1,41 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { scanText } from './release-scan.mjs';
4
+
5
+ const COAUTHOR = 'Co-' + 'Authored-By: Claude <noreply@anthropic.com>';
6
+
7
+ describe('scanText — attribution detection', () => {
8
+ it('flags a co-author trailer', () => {
9
+ const f = scanText(`fix: thing\n\n${COAUTHOR}\n`);
10
+ assert.ok(f.some((x) => x.kind === 'attribution' && /co-author/.test(x.detail)));
11
+ });
12
+
13
+ it('flags an AI "Generated with" footer', () => {
14
+ const f = scanText('🤖 Generated with Claude Code\n');
15
+ assert.ok(f.some((x) => x.kind === 'attribution'));
16
+ });
17
+
18
+ it('flags "Reviewed by <AI>"', () => {
19
+ const f = scanText('Reviewed by Codex and approved.\n');
20
+ assert.ok(f.some((x) => x.kind === 'attribution' && /review/.test(x.detail)));
21
+ });
22
+
23
+ it('does NOT flag a legitimate review-tool description', () => {
24
+ const ok = 'You are a meticulous staff-level reviewer giving a second opinion.\n';
25
+ assert.deepEqual(scanText(ok), []);
26
+ });
27
+
28
+ it('does NOT flag "auto-generated navigator"', () => {
29
+ assert.deepEqual(scanText('the auto-generated index.md navigator\n'), []);
30
+ });
31
+
32
+ it('does NOT flag legitimate product names (Claude, Codex, Gemini, GPT-OSS) as prose', () => {
33
+ const ok = 'agy reaches Gemini, Claude, and GPT-OSS; codex-exec runs in a sandbox.\n';
34
+ assert.deepEqual(scanText(ok), []);
35
+ });
36
+
37
+ it('does NOT flag clean prose with emoji, math symbols, em-dash, and accents', () => {
38
+ const clean = '## 🧭 Memory Map — caps ≤ 100, provides ⊇ roles · café façade naïve\n';
39
+ assert.deepEqual(scanText(clean), []);
40
+ });
41
+ });