@iceinvein/agent-skills 0.1.39 → 0.2.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 (148) hide show
  1. package/README.md +18 -2
  2. package/dist/cli/index.js +105 -28
  3. package/package.json +1 -1
  4. package/skills/index.json +14 -2
  5. package/skills/magpie/README.md +2 -1
  6. package/skills/magpie/SKILL.md +138 -45
  7. package/skills/magpie/bin/magpie.ts +43 -0
  8. package/skills/magpie/fixtures/example-pr/brief.json +18 -0
  9. package/skills/magpie/fixtures/fake-gh-nodiff.sh +38 -0
  10. package/skills/magpie/fixtures/fake-gh.sh +14 -0
  11. package/skills/magpie/package.json +1 -1
  12. package/skills/magpie/references/peer-review.md +7 -2
  13. package/skills/magpie/references/scout.md +90 -0
  14. package/skills/magpie/references/specialists.md +117 -6
  15. package/skills/magpie/scripts/__tests__/cli.test.ts +101 -1
  16. package/skills/magpie/scripts/__tests__/dedupe-cmd.test.ts +187 -0
  17. package/skills/magpie/scripts/__tests__/diff-chunks.test.ts +51 -0
  18. package/skills/magpie/scripts/__tests__/filter-diff-preservation.test.ts +54 -0
  19. package/skills/magpie/scripts/__tests__/findings-files.test.ts +35 -0
  20. package/skills/magpie/scripts/__tests__/gh.test.ts +90 -0
  21. package/skills/magpie/scripts/__tests__/git-diff.test.ts +83 -0
  22. package/skills/magpie/scripts/__tests__/helpers/git-fixture.ts +47 -0
  23. package/skills/magpie/scripts/__tests__/path-filter.test.ts +27 -0
  24. package/skills/magpie/scripts/__tests__/preview-cmd.test.ts +16 -0
  25. package/skills/magpie/scripts/__tests__/refresh.test.ts +45 -0
  26. package/skills/magpie/scripts/__tests__/render-cmd.test.ts +173 -1
  27. package/skills/magpie/scripts/__tests__/render-findings.test.ts +151 -1
  28. package/skills/magpie/scripts/__tests__/render-progress.test.ts +42 -0
  29. package/skills/magpie/scripts/__tests__/setup-cmd.test.ts +83 -1
  30. package/skills/magpie/scripts/__tests__/shard.test.ts +165 -0
  31. package/skills/magpie/scripts/__tests__/skill-lint.test.ts +207 -7
  32. package/skills/magpie/scripts/__tests__/types.test.ts +47 -0
  33. package/skills/magpie/scripts/dedupe-cmd.ts +58 -3
  34. package/skills/magpie/scripts/diff-chunks.ts +28 -0
  35. package/skills/magpie/scripts/findings-files.ts +32 -0
  36. package/skills/magpie/scripts/gh.ts +68 -14
  37. package/skills/magpie/scripts/git-diff.ts +111 -0
  38. package/skills/magpie/scripts/path-filter.ts +9 -5
  39. package/skills/magpie/scripts/preview-cmd.ts +11 -1
  40. package/skills/magpie/scripts/refresh.ts +32 -3
  41. package/skills/magpie/scripts/render-cmd.ts +35 -11
  42. package/skills/magpie/scripts/render-findings.ts +78 -2
  43. package/skills/magpie/scripts/render-progress.ts +6 -1
  44. package/skills/magpie/scripts/setup-cmd.ts +38 -1
  45. package/skills/magpie/scripts/shard.ts +171 -0
  46. package/skills/magpie/scripts/status-cmd.ts +7 -4
  47. package/skills/magpie/scripts/types.ts +50 -0
  48. package/skills/magpie/skill.json +2 -2
  49. package/skills/magpie/templates/styles.css +66 -0
  50. package/skills/migrate/README.md +194 -0
  51. package/skills/migrate/SKILL.md +197 -0
  52. package/skills/migrate/bin/migrate +15 -0
  53. package/skills/migrate/bin/migrate.ts +309 -0
  54. package/skills/migrate/biome.json +35 -0
  55. package/skills/migrate/bun.lock +24 -0
  56. package/skills/migrate/docs/architecture.md +294 -0
  57. package/skills/migrate/docs/reference.md +590 -0
  58. package/skills/migrate/fixtures/tiny-express/GROUND-TRUTH.md +39 -0
  59. package/skills/migrate/fixtures/tiny-express/app.js +29 -0
  60. package/skills/migrate/fixtures/tiny-express/cron.js +6 -0
  61. package/skills/migrate/fixtures/tiny-express/reports/daily-users.json +6 -0
  62. package/skills/migrate/fixtures/tiny-express/schema.sql +12 -0
  63. package/skills/migrate/fixtures/tiny-express/settings.json +4 -0
  64. package/skills/migrate/fixtures/tiny-express/views/users.html +9 -0
  65. package/skills/migrate/fixtures/tiny-webforms/Controllers/UsersController.cs +68 -0
  66. package/skills/migrate/fixtures/tiny-webforms/Default.aspx +7 -0
  67. package/skills/migrate/fixtures/tiny-webforms/Default.aspx.cs +14 -0
  68. package/skills/migrate/fixtures/tiny-webforms/GROUND-TRUTH.md +50 -0
  69. package/skills/migrate/fixtures/tiny-webforms/Integrations/BillingClient.cs +16 -0
  70. package/skills/migrate/fixtures/tiny-webforms/Jobs/NightlyDigestJob.cs +33 -0
  71. package/skills/migrate/fixtures/tiny-webforms/Reports/DailyUsers.rdl +11 -0
  72. package/skills/migrate/fixtures/tiny-webforms/Schema.sql +12 -0
  73. package/skills/migrate/fixtures/tiny-webforms/Site.master +16 -0
  74. package/skills/migrate/fixtures/tiny-webforms/Users.aspx +8 -0
  75. package/skills/migrate/fixtures/tiny-webforms/Users.aspx.cs +14 -0
  76. package/skills/migrate/fixtures/tiny-webforms/web.config +10 -0
  77. package/skills/migrate/install.sh +68 -0
  78. package/skills/migrate/package.json +17 -0
  79. package/skills/migrate/references/phases/enumerate.md +291 -0
  80. package/skills/migrate/references/phases/extract.md +652 -0
  81. package/skills/migrate/references/phases/parity.md +275 -0
  82. package/skills/migrate/references/phases/probe.md +135 -0
  83. package/skills/migrate/references/phases/queue.md +242 -0
  84. package/skills/migrate/references/phases/seam.md +416 -0
  85. package/skills/migrate/references/recipes/README.md +116 -0
  86. package/skills/migrate/references/recipes/aspnet.md +287 -0
  87. package/skills/migrate/references/run-ops.md +280 -0
  88. package/skills/migrate/scripts/__tests__/census.test.ts +775 -0
  89. package/skills/migrate/scripts/__tests__/check.test.ts +458 -0
  90. package/skills/migrate/scripts/__tests__/citations.test.ts +156 -0
  91. package/skills/migrate/scripts/__tests__/cli.test.ts +183 -0
  92. package/skills/migrate/scripts/__tests__/concurrency.test.ts +164 -0
  93. package/skills/migrate/scripts/__tests__/config.test.ts +112 -0
  94. package/skills/migrate/scripts/__tests__/e2e-express.test.ts +1093 -0
  95. package/skills/migrate/scripts/__tests__/e2e-webforms.test.ts +1276 -0
  96. package/skills/migrate/scripts/__tests__/e2e.test.ts +320 -0
  97. package/skills/migrate/scripts/__tests__/ids.test.ts +38 -0
  98. package/skills/migrate/scripts/__tests__/import.test.ts +155 -0
  99. package/skills/migrate/scripts/__tests__/init.test.ts +192 -0
  100. package/skills/migrate/scripts/__tests__/leaks.test.ts +176 -0
  101. package/skills/migrate/scripts/__tests__/lock.test.ts +183 -0
  102. package/skills/migrate/scripts/__tests__/paths.test.ts +129 -0
  103. package/skills/migrate/scripts/__tests__/phase-cmd.test.ts +151 -0
  104. package/skills/migrate/scripts/__tests__/phases.test.ts +70 -0
  105. package/skills/migrate/scripts/__tests__/queue.test.ts +475 -0
  106. package/skills/migrate/scripts/__tests__/report.test.ts +150 -0
  107. package/skills/migrate/scripts/__tests__/run-state.test.ts +136 -0
  108. package/skills/migrate/scripts/__tests__/status-reset.test.ts +318 -0
  109. package/skills/migrate/scripts/__tests__/store.test.ts +132 -0
  110. package/skills/migrate/scripts/__tests__/validate.test.ts +54 -0
  111. package/skills/migrate/scripts/census-cmd.ts +109 -0
  112. package/skills/migrate/scripts/census.ts +342 -0
  113. package/skills/migrate/scripts/check-cmd.ts +24 -0
  114. package/skills/migrate/scripts/check.ts +376 -0
  115. package/skills/migrate/scripts/citations.ts +92 -0
  116. package/skills/migrate/scripts/config.ts +237 -0
  117. package/skills/migrate/scripts/ids.ts +31 -0
  118. package/skills/migrate/scripts/import-cmd.ts +141 -0
  119. package/skills/migrate/scripts/init-cmd.ts +118 -0
  120. package/skills/migrate/scripts/leaks.ts +184 -0
  121. package/skills/migrate/scripts/lock.ts +188 -0
  122. package/skills/migrate/scripts/paths.ts +103 -0
  123. package/skills/migrate/scripts/phase-cmd.ts +63 -0
  124. package/skills/migrate/scripts/phases.ts +113 -0
  125. package/skills/migrate/scripts/queue-cmd.ts +98 -0
  126. package/skills/migrate/scripts/queue.ts +258 -0
  127. package/skills/migrate/scripts/report-cmd.ts +47 -0
  128. package/skills/migrate/scripts/report.ts +131 -0
  129. package/skills/migrate/scripts/reset-cmd.ts +120 -0
  130. package/skills/migrate/scripts/status-cmd.ts +52 -0
  131. package/skills/migrate/scripts/store.ts +159 -0
  132. package/skills/migrate/scripts/types.ts +137 -0
  133. package/skills/migrate/scripts/validate.ts +221 -0
  134. package/skills/migrate/skill.json +33 -0
  135. package/skills/migrate/templates/config.toml +27 -0
  136. package/skills/migrate/templates/queue-item.md +17 -0
  137. package/skills/migrate/tsconfig.json +18 -0
  138. package/skills/migrate/uninstall.sh +31 -0
  139. package/skills/sluice/SKILL.md +82 -0
  140. package/skills/sluice/references/deep-channel.md +94 -0
  141. package/skills/sluice/references/finish.md +35 -0
  142. package/skills/sluice/references/intent.md +29 -0
  143. package/skills/sluice/references/review.md +42 -0
  144. package/skills/sluice/references/root-cause.md +38 -0
  145. package/skills/sluice/references/show-or-say.md +36 -0
  146. package/skills/sluice/references/test-first.md +35 -0
  147. package/skills/sluice/references/verify.md +26 -0
  148. package/skills/sluice/skill.json +32 -0
@@ -0,0 +1,151 @@
1
+ import { afterEach, beforeEach, expect, test } from 'bun:test'
2
+ import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+
6
+ const CLI = join(import.meta.dir, '..', '..', 'bin', 'migrate.ts')
7
+
8
+ let target: string
9
+ let source: string
10
+
11
+ async function migrate(args: string[]): Promise<{ code: number; out: string; err: string }> {
12
+ const proc = Bun.spawn(['bun', CLI, ...args], { cwd: target, stdout: 'pipe', stderr: 'pipe' })
13
+ const [out, err] = await Promise.all([
14
+ new Response(proc.stdout).text(),
15
+ new Response(proc.stderr).text(),
16
+ ])
17
+ await proc.exited
18
+ return { code: proc.exitCode ?? -1, out, err }
19
+ }
20
+
21
+ beforeEach(async () => {
22
+ target = await mkdtemp(join(tmpdir(), 'migrate-phase-'))
23
+ source = join(target, 'legacy')
24
+ await Bun.write(join(source, 'app.js'), '// legacy\n')
25
+ const init = await migrate(['init', '--source', source, '--scope', 'x', '--name', 'target'])
26
+ expect(init.code).toBe(0)
27
+ })
28
+
29
+ afterEach(async () => {
30
+ await rm(target, { recursive: true, force: true })
31
+ })
32
+
33
+ test('phase with no arguments prints every phase and its status', async () => {
34
+ const result = await migrate(['phase'])
35
+ expect(result.code).toBe(0)
36
+ for (const p of [
37
+ 'probe',
38
+ 'enumerate',
39
+ 'seam',
40
+ 'extract',
41
+ 'parity',
42
+ 'queue',
43
+ 'adjudicate',
44
+ 'handoff',
45
+ ]) {
46
+ expect(result.out).toContain(p)
47
+ }
48
+ expect(result.out).toContain('pending')
49
+ })
50
+
51
+ test('phase <name> --status done persists to phases.json', async () => {
52
+ const set = await migrate(['phase', 'probe', '--status', 'done'])
53
+ expect(set.code).toBe(0)
54
+ expect(set.out).toContain('probe is now done')
55
+ const phases = JSON.parse(await readFile(join(target, '.migrate', 'phases.json'), 'utf8'))
56
+ expect(phases.phases.probe.status).toBe('done')
57
+ })
58
+
59
+ test('phase <name> with no status prints just that phase', async () => {
60
+ await migrate(['phase', 'seam', '--status', 'running'])
61
+ const result = await migrate(['phase', 'seam'])
62
+ expect(result.code).toBe(0)
63
+ expect(result.out).toContain('seam')
64
+ expect(result.out).toContain('running')
65
+ expect(result.out).not.toContain('probe')
66
+ })
67
+
68
+ test('an unknown phase name is a usage error naming the valid set', async () => {
69
+ const result = await migrate(['phase', 'nonsense', '--status', 'done'])
70
+ expect(result.code).toBe(2)
71
+ expect(result.err).toContain('nonsense')
72
+ expect(result.err).toContain('enumerate')
73
+ })
74
+
75
+ test('an unknown status is a usage error naming the valid set', async () => {
76
+ const result = await migrate(['phase', 'probe', '--status', 'finished'])
77
+ expect(result.code).toBe(2)
78
+ expect(result.err).toContain('finished')
79
+ expect(result.err).toContain('blocked')
80
+ })
81
+
82
+ // Final-review finding, Minor 3: `phase --force-unlock <name> --status <s>`
83
+ // put a flag in the slot the phase name is read from, so the name came back
84
+ // undefined, the write silently became a read, and the command exited 0
85
+ // having moved nothing. `import` and `census` both reject the identical
86
+ // flag-ordering mistake at 2, because their first positional is load-bearing
87
+ // too.
88
+ test('a write-intent flag before the phase name is a usage error, not a silent listing', async () => {
89
+ // Set a status the correct way first, so phases.json exists and carries a
90
+ // value the mis-ordered invocation below would visibly overwrite if it were
91
+ // still being serviced as a write.
92
+ expect((await migrate(['phase', 'probe', '--status', 'running'])).code).toBe(0)
93
+
94
+ const result = await migrate(['phase', '--force-unlock', 'probe', '--status', 'done'])
95
+ expect(result.code).toBe(2)
96
+ expect(result.err).toContain('want <name> before --status/--force-unlock')
97
+ const phases = JSON.parse(await readFile(join(target, '.migrate', 'phases.json'), 'utf8'))
98
+ expect(phases.phases.probe.status).toBe('running')
99
+ })
100
+
101
+ test('--status with no phase name at all is the same usage error', async () => {
102
+ const result = await migrate(['phase', '--status', 'done'])
103
+ expect(result.code).toBe(2)
104
+ expect(result.err).toContain('want <name> before --status/--force-unlock')
105
+ })
106
+
107
+ test('recording a census commits its batch under the record phase', async () => {
108
+ const record = join(target, 'census.json')
109
+ await writeFile(
110
+ record,
111
+ JSON.stringify({
112
+ kind: 'lens',
113
+ surface: 'routes',
114
+ phase: 'enumerate',
115
+ directions: {
116
+ code: { count: 2, evidence: 'rg -n "app.(get|post)" app.js' },
117
+ nav: { count: 2, evidence: 'read views/index.html link targets' },
118
+ },
119
+ total: 2,
120
+ in_ledger: 0,
121
+ added: 2,
122
+ skipped: [],
123
+ queued: [],
124
+ batch: 'b-routes-census-001',
125
+ }),
126
+ )
127
+ const result = await migrate(['census', record])
128
+ expect(result.code).toBe(0)
129
+ const phases = JSON.parse(await readFile(join(target, '.migrate', 'phases.json'), 'utf8'))
130
+ const ids = (phases.phases.enumerate.batches as { id: string }[]).map((b) => b.id)
131
+ expect(ids).toContain('b-routes-census-001')
132
+ })
133
+
134
+ test('a closer census without a phase is rejected', async () => {
135
+ const record = join(target, 'closer.json')
136
+ await writeFile(
137
+ record,
138
+ JSON.stringify({
139
+ kind: 'closer',
140
+ closer: 'scope-injection',
141
+ batch: 'b-closer-001',
142
+ checked: 3,
143
+ findings: 0,
144
+ fixed: 0,
145
+ queued: [],
146
+ }),
147
+ )
148
+ const result = await migrate(['census', record])
149
+ expect(result.code).toBe(2)
150
+ expect(result.err).toContain('phase is required')
151
+ })
@@ -0,0 +1,70 @@
1
+ import { afterEach, beforeEach, expect, test } from 'bun:test'
2
+ import { mkdir, mkdtemp, readdir, rm, writeFile } from 'node:fs/promises'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+ import { isPhase, loadPhases, recordBatch, savePhases, setPhaseStatus } from '../phases.ts'
6
+
7
+ let root: string
8
+ const NO_SOURCE = '/nonexistent-source'
9
+
10
+ beforeEach(async () => {
11
+ root = await mkdtemp(join(tmpdir(), 'migrate-phases-'))
12
+ await mkdir(join(root, '.migrate'), { recursive: true })
13
+ })
14
+
15
+ afterEach(async () => {
16
+ await rm(root, { recursive: true, force: true })
17
+ })
18
+
19
+ test('loadPhases defaults every phase to pending', async () => {
20
+ const state = await loadPhases(root)
21
+ expect(state.enumerate.status).toBe('pending')
22
+ expect(state.handoff.batches).toEqual([])
23
+ })
24
+
25
+ test('recordBatch appends and flips the phase to running', async () => {
26
+ await recordBatch(root, 'enumerate', { id: 'b-routes-code-001', count: 10 }, NO_SOURCE)
27
+ await recordBatch(root, 'enumerate', { id: 'b-routes-code-002', count: 7 }, NO_SOURCE)
28
+ const state = await loadPhases(root)
29
+ expect(state.enumerate.status).toBe('running')
30
+ expect(state.enumerate.batches.map((b) => b.id)).toEqual([
31
+ 'b-routes-code-001',
32
+ 'b-routes-code-002',
33
+ ])
34
+ })
35
+
36
+ test('recordBatch is idempotent on a repeated batch id', async () => {
37
+ await recordBatch(root, 'enumerate', { id: 'b-1', count: 10 }, NO_SOURCE)
38
+ await recordBatch(root, 'enumerate', { id: 'b-1', count: 10 }, NO_SOURCE)
39
+ const state = await loadPhases(root)
40
+ expect(state.enumerate.batches).toHaveLength(1)
41
+ })
42
+
43
+ test('recordBatch does not reopen a done phase', async () => {
44
+ await setPhaseStatus(root, 'enumerate', 'done', NO_SOURCE)
45
+ await recordBatch(root, 'enumerate', { id: 'b-late', count: 1 }, NO_SOURCE)
46
+ const state = await loadPhases(root)
47
+ expect(state.enumerate.status).toBe('done')
48
+ expect(state.enumerate.batches).toHaveLength(1)
49
+ })
50
+
51
+ test('isPhase rejects an unknown phase name', () => {
52
+ expect(isPhase('seam')).toBe(true)
53
+ expect(isPhase('seams')).toBe(false)
54
+ })
55
+
56
+ test('loadPhases names the file on malformed JSON', async () => {
57
+ const path = join(root, '.migrate', 'phases.json')
58
+ await writeFile(path, '{ this is not json')
59
+ await expect(loadPhases(root)).rejects.toThrow(/phases.json: malformed JSON/)
60
+ })
61
+
62
+ test('savePhases cleans up temp file on failure', async () => {
63
+ const emptyState = await loadPhases(root)
64
+ const phaseFile = join(root, '.migrate', 'phases.json')
65
+ await mkdir(phaseFile)
66
+ await expect(savePhases(root, emptyState, NO_SOURCE)).rejects.toThrow()
67
+ const migrateFiles = await readdir(join(root, '.migrate'))
68
+ const tmpFiles = migrateFiles.filter((f) => f.endsWith('.tmp'))
69
+ expect(tmpFiles.length).toBe(0)
70
+ })