@kamilmarzynski/scifi 0.1.1

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 (257) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +232 -0
  3. package/dist/skills/sf-bug/manifest.d.ts +2 -0
  4. package/dist/skills/sf-bug/manifest.js +6 -0
  5. package/dist/skills/sf-bug/manifest.js.map +1 -0
  6. package/dist/skills/sf-change/manifest.d.ts +2 -0
  7. package/dist/skills/sf-change/manifest.js +6 -0
  8. package/dist/skills/sf-change/manifest.js.map +1 -0
  9. package/dist/skills/sf-code-review/manifest.d.ts +2 -0
  10. package/dist/skills/sf-code-review/manifest.js +5 -0
  11. package/dist/skills/sf-code-review/manifest.js.map +1 -0
  12. package/dist/skills/sf-continue/manifest.d.ts +2 -0
  13. package/dist/skills/sf-continue/manifest.js +6 -0
  14. package/dist/skills/sf-continue/manifest.js.map +1 -0
  15. package/dist/skills/sf-feature/manifest.d.ts +2 -0
  16. package/dist/skills/sf-feature/manifest.js +6 -0
  17. package/dist/skills/sf-feature/manifest.js.map +1 -0
  18. package/dist/skills/sf-fix/manifest.d.ts +2 -0
  19. package/dist/skills/sf-fix/manifest.js +6 -0
  20. package/dist/skills/sf-fix/manifest.js.map +1 -0
  21. package/dist/skills/sf-handover/manifest.d.ts +2 -0
  22. package/dist/skills/sf-handover/manifest.js +5 -0
  23. package/dist/skills/sf-handover/manifest.js.map +1 -0
  24. package/dist/skills/sf-implement/manifest.d.ts +2 -0
  25. package/dist/skills/sf-implement/manifest.js +6 -0
  26. package/dist/skills/sf-implement/manifest.js.map +1 -0
  27. package/dist/skills/sf-plan/manifest.d.ts +2 -0
  28. package/dist/skills/sf-plan/manifest.js +6 -0
  29. package/dist/skills/sf-plan/manifest.js.map +1 -0
  30. package/dist/skills/sf-plan-review/manifest.d.ts +2 -0
  31. package/dist/skills/sf-plan-review/manifest.js +5 -0
  32. package/dist/skills/sf-plan-review/manifest.js.map +1 -0
  33. package/dist/skills/sf-receiving-review/manifest.d.ts +2 -0
  34. package/dist/skills/sf-receiving-review/manifest.js +5 -0
  35. package/dist/skills/sf-receiving-review/manifest.js.map +1 -0
  36. package/dist/skills/sf-spec-review/manifest.d.ts +2 -0
  37. package/dist/skills/sf-spec-review/manifest.js +5 -0
  38. package/dist/skills/sf-spec-review/manifest.js.map +1 -0
  39. package/dist/skills/sf-tdd/manifest.d.ts +2 -0
  40. package/dist/skills/sf-tdd/manifest.js +5 -0
  41. package/dist/skills/sf-tdd/manifest.js.map +1 -0
  42. package/dist/skills/sf-verification/manifest.d.ts +2 -0
  43. package/dist/skills/sf-verification/manifest.js +5 -0
  44. package/dist/skills/sf-verification/manifest.js.map +1 -0
  45. package/dist/src/cli/commands/bug.d.ts +2 -0
  46. package/dist/src/cli/commands/bug.js +58 -0
  47. package/dist/src/cli/commands/bug.js.map +1 -0
  48. package/dist/src/cli/commands/finish.d.ts +2 -0
  49. package/dist/src/cli/commands/finish.js +43 -0
  50. package/dist/src/cli/commands/finish.js.map +1 -0
  51. package/dist/src/cli/commands/fix.d.ts +2 -0
  52. package/dist/src/cli/commands/fix.js +60 -0
  53. package/dist/src/cli/commands/fix.js.map +1 -0
  54. package/dist/src/cli/commands/init.d.ts +2 -0
  55. package/dist/src/cli/commands/init.js +92 -0
  56. package/dist/src/cli/commands/init.js.map +1 -0
  57. package/dist/src/cli/commands/list.d.ts +2 -0
  58. package/dist/src/cli/commands/list.js +52 -0
  59. package/dist/src/cli/commands/list.js.map +1 -0
  60. package/dist/src/cli/commands/plan-ready.d.ts +2 -0
  61. package/dist/src/cli/commands/plan-ready.js +27 -0
  62. package/dist/src/cli/commands/plan-ready.js.map +1 -0
  63. package/dist/src/cli/commands/plan.d.ts +2 -0
  64. package/dist/src/cli/commands/plan.js +43 -0
  65. package/dist/src/cli/commands/plan.js.map +1 -0
  66. package/dist/src/cli/commands/spec-ready.d.ts +2 -0
  67. package/dist/src/cli/commands/spec-ready.js +27 -0
  68. package/dist/src/cli/commands/spec-ready.js.map +1 -0
  69. package/dist/src/cli/commands/spec.d.ts +2 -0
  70. package/dist/src/cli/commands/spec.js +46 -0
  71. package/dist/src/cli/commands/spec.js.map +1 -0
  72. package/dist/src/cli/commands/start.d.ts +2 -0
  73. package/dist/src/cli/commands/start.js +27 -0
  74. package/dist/src/cli/commands/start.js.map +1 -0
  75. package/dist/src/cli/commands/status.d.ts +2 -0
  76. package/dist/src/cli/commands/status.js +62 -0
  77. package/dist/src/cli/commands/status.js.map +1 -0
  78. package/dist/src/cli/commands/task.d.ts +2 -0
  79. package/dist/src/cli/commands/task.js +64 -0
  80. package/dist/src/cli/commands/task.js.map +1 -0
  81. package/dist/src/cli/commands/worktree.d.ts +2 -0
  82. package/dist/src/cli/commands/worktree.js +33 -0
  83. package/dist/src/cli/commands/worktree.js.map +1 -0
  84. package/dist/src/cli/index.d.ts +3 -0
  85. package/dist/src/cli/index.js +106 -0
  86. package/dist/src/cli/index.js.map +1 -0
  87. package/dist/src/core/bugs/create.d.ts +13 -0
  88. package/dist/src/core/bugs/create.js +28 -0
  89. package/dist/src/core/bugs/create.js.map +1 -0
  90. package/dist/src/core/bugs/frontmatter.d.ts +7 -0
  91. package/dist/src/core/bugs/frontmatter.js +65 -0
  92. package/dist/src/core/bugs/frontmatter.js.map +1 -0
  93. package/dist/src/core/bugs/id.d.ts +1 -0
  94. package/dist/src/core/bugs/id.js +4 -0
  95. package/dist/src/core/bugs/id.js.map +1 -0
  96. package/dist/src/core/bugs/paths.d.ts +2 -0
  97. package/dist/src/core/bugs/paths.js +8 -0
  98. package/dist/src/core/bugs/paths.js.map +1 -0
  99. package/dist/src/core/bugs/types.d.ts +12 -0
  100. package/dist/src/core/bugs/types.js +3 -0
  101. package/dist/src/core/bugs/types.js.map +1 -0
  102. package/dist/src/core/fixes/create.d.ts +11 -0
  103. package/dist/src/core/fixes/create.js +43 -0
  104. package/dist/src/core/fixes/create.js.map +1 -0
  105. package/dist/src/core/fixes/frontmatter.d.ts +7 -0
  106. package/dist/src/core/fixes/frontmatter.js +50 -0
  107. package/dist/src/core/fixes/frontmatter.js.map +1 -0
  108. package/dist/src/core/fixes/id.d.ts +1 -0
  109. package/dist/src/core/fixes/id.js +4 -0
  110. package/dist/src/core/fixes/id.js.map +1 -0
  111. package/dist/src/core/fixes/list.d.ts +8 -0
  112. package/dist/src/core/fixes/list.js +43 -0
  113. package/dist/src/core/fixes/list.js.map +1 -0
  114. package/dist/src/core/fixes/paths.d.ts +2 -0
  115. package/dist/src/core/fixes/paths.js +9 -0
  116. package/dist/src/core/fixes/paths.js.map +1 -0
  117. package/dist/src/core/fixes/transition.d.ts +9 -0
  118. package/dist/src/core/fixes/transition.js +26 -0
  119. package/dist/src/core/fixes/transition.js.map +1 -0
  120. package/dist/src/core/fixes/types.d.ts +9 -0
  121. package/dist/src/core/fixes/types.js +2 -0
  122. package/dist/src/core/fixes/types.js.map +1 -0
  123. package/dist/src/core/init/config.d.ts +6 -0
  124. package/dist/src/core/init/config.js +18 -0
  125. package/dist/src/core/init/config.js.map +1 -0
  126. package/dist/src/core/init/install-skills.d.ts +8 -0
  127. package/dist/src/core/init/install-skills.js +14 -0
  128. package/dist/src/core/init/install-skills.js.map +1 -0
  129. package/dist/src/core/init/prompt-harness.d.ts +8 -0
  130. package/dist/src/core/init/prompt-harness.js +19 -0
  131. package/dist/src/core/init/prompt-harness.js.map +1 -0
  132. package/dist/src/core/init/scaffold.d.ts +5 -0
  133. package/dist/src/core/init/scaffold.js +91 -0
  134. package/dist/src/core/init/scaffold.js.map +1 -0
  135. package/dist/src/core/init/types.d.ts +5 -0
  136. package/dist/src/core/init/types.js +2 -0
  137. package/dist/src/core/init/types.js.map +1 -0
  138. package/dist/src/core/output/emit.d.ts +8 -0
  139. package/dist/src/core/output/emit.js +50 -0
  140. package/dist/src/core/output/emit.js.map +1 -0
  141. package/dist/src/core/output/errors.d.ts +14 -0
  142. package/dist/src/core/output/errors.js +36 -0
  143. package/dist/src/core/output/errors.js.map +1 -0
  144. package/dist/src/core/output/index.d.ts +4 -0
  145. package/dist/src/core/output/index.js +4 -0
  146. package/dist/src/core/output/index.js.map +1 -0
  147. package/dist/src/core/output/tty.d.ts +1 -0
  148. package/dist/src/core/output/tty.js +4 -0
  149. package/dist/src/core/output/tty.js.map +1 -0
  150. package/dist/src/core/package-root.d.ts +1 -0
  151. package/dist/src/core/package-root.js +18 -0
  152. package/dist/src/core/package-root.js.map +1 -0
  153. package/dist/src/core/skills/catalog.d.ts +6 -0
  154. package/dist/src/core/skills/catalog.js +69 -0
  155. package/dist/src/core/skills/catalog.js.map +1 -0
  156. package/dist/src/core/skills/harness/adapter.d.ts +15 -0
  157. package/dist/src/core/skills/harness/adapter.js +31 -0
  158. package/dist/src/core/skills/harness/adapter.js.map +1 -0
  159. package/dist/src/core/skills/harness/claude-code.d.ts +2 -0
  160. package/dist/src/core/skills/harness/claude-code.js +37 -0
  161. package/dist/src/core/skills/harness/claude-code.js.map +1 -0
  162. package/dist/src/core/skills/harness/register-defaults.d.ts +1 -0
  163. package/dist/src/core/skills/harness/register-defaults.js +4 -0
  164. package/dist/src/core/skills/harness/register-defaults.js.map +1 -0
  165. package/dist/src/core/skills/harness/registry.d.ts +3 -0
  166. package/dist/src/core/skills/harness/registry.js +22 -0
  167. package/dist/src/core/skills/harness/registry.js.map +1 -0
  168. package/dist/src/core/skills/types.d.ts +18 -0
  169. package/dist/src/core/skills/types.js +9 -0
  170. package/dist/src/core/skills/types.js.map +1 -0
  171. package/dist/src/core/slugify.d.ts +2 -0
  172. package/dist/src/core/slugify.js +13 -0
  173. package/dist/src/core/slugify.js.map +1 -0
  174. package/dist/src/core/specs/create.d.ts +11 -0
  175. package/dist/src/core/specs/create.js +35 -0
  176. package/dist/src/core/specs/create.js.map +1 -0
  177. package/dist/src/core/specs/id.d.ts +1 -0
  178. package/dist/src/core/specs/id.js +4 -0
  179. package/dist/src/core/specs/id.js.map +1 -0
  180. package/dist/src/core/specs/lifecycle.d.ts +17 -0
  181. package/dist/src/core/specs/lifecycle.js +97 -0
  182. package/dist/src/core/specs/lifecycle.js.map +1 -0
  183. package/dist/src/core/specs/list.d.ts +6 -0
  184. package/dist/src/core/specs/list.js +43 -0
  185. package/dist/src/core/specs/list.js.map +1 -0
  186. package/dist/src/core/specs/metadata.d.ts +3 -0
  187. package/dist/src/core/specs/metadata.js +13 -0
  188. package/dist/src/core/specs/metadata.js.map +1 -0
  189. package/dist/src/core/specs/paths.d.ts +3 -0
  190. package/dist/src/core/specs/paths.js +13 -0
  191. package/dist/src/core/specs/paths.js.map +1 -0
  192. package/dist/src/core/specs/plan-session.d.ts +18 -0
  193. package/dist/src/core/specs/plan-session.js +24 -0
  194. package/dist/src/core/specs/plan-session.js.map +1 -0
  195. package/dist/src/core/specs/transition.d.ts +8 -0
  196. package/dist/src/core/specs/transition.js +33 -0
  197. package/dist/src/core/specs/transition.js.map +1 -0
  198. package/dist/src/core/specs/types.d.ts +17 -0
  199. package/dist/src/core/specs/types.js +8 -0
  200. package/dist/src/core/specs/types.js.map +1 -0
  201. package/dist/src/core/specs/worktree.d.ts +10 -0
  202. package/dist/src/core/specs/worktree.js +32 -0
  203. package/dist/src/core/specs/worktree.js.map +1 -0
  204. package/dist/src/core/tasks/frontmatter.d.ts +7 -0
  205. package/dist/src/core/tasks/frontmatter.js +53 -0
  206. package/dist/src/core/tasks/frontmatter.js.map +1 -0
  207. package/dist/src/core/tasks/list.d.ts +2 -0
  208. package/dist/src/core/tasks/list.js +18 -0
  209. package/dist/src/core/tasks/list.js.map +1 -0
  210. package/dist/src/core/tasks/paths.d.ts +2 -0
  211. package/dist/src/core/tasks/paths.js +11 -0
  212. package/dist/src/core/tasks/paths.js.map +1 -0
  213. package/dist/src/core/tasks/transition.d.ts +8 -0
  214. package/dist/src/core/tasks/transition.js +30 -0
  215. package/dist/src/core/tasks/transition.js.map +1 -0
  216. package/dist/src/core/tasks/types.d.ts +8 -0
  217. package/dist/src/core/tasks/types.js +2 -0
  218. package/dist/src/core/tasks/types.js.map +1 -0
  219. package/package.json +67 -0
  220. package/skills/sf-bug/DISPATCH-CODE-REVIEW.md +22 -0
  221. package/skills/sf-bug/body.md +117 -0
  222. package/skills/sf-bug/manifest.ts +8 -0
  223. package/skills/sf-change/body.md +178 -0
  224. package/skills/sf-change/manifest.ts +8 -0
  225. package/skills/sf-code-review/body.md +155 -0
  226. package/skills/sf-code-review/manifest.ts +7 -0
  227. package/skills/sf-continue/body.md +90 -0
  228. package/skills/sf-continue/manifest.ts +8 -0
  229. package/skills/sf-feature/ADR-TEMPLATE.md +16 -0
  230. package/skills/sf-feature/DISPATCH-SPEC-REVIEW.md +16 -0
  231. package/skills/sf-feature/SPEC-TEMPLATE.md +37 -0
  232. package/skills/sf-feature/body.md +145 -0
  233. package/skills/sf-feature/manifest.ts +8 -0
  234. package/skills/sf-fix/DISPATCH-CODE-REVIEW.md +25 -0
  235. package/skills/sf-fix/body.md +174 -0
  236. package/skills/sf-fix/manifest.ts +8 -0
  237. package/skills/sf-handover/body.md +67 -0
  238. package/skills/sf-handover/manifest.ts +7 -0
  239. package/skills/sf-implement/DISPATCH-CODE-REVIEW.md +19 -0
  240. package/skills/sf-implement/DISPATCH-HANDOVER.md +19 -0
  241. package/skills/sf-implement/DISPATCH-IMPLEMENTER.md +36 -0
  242. package/skills/sf-implement/body.md +147 -0
  243. package/skills/sf-implement/manifest.ts +8 -0
  244. package/skills/sf-plan/ADR-TEMPLATE.md +16 -0
  245. package/skills/sf-plan/DESIGN-TEMPLATE.md +54 -0
  246. package/skills/sf-plan/DISPATCH-PLAN-REVIEW.md +17 -0
  247. package/skills/sf-plan/TASK-TEMPLATE.md +30 -0
  248. package/skills/sf-plan/body.md +162 -0
  249. package/skills/sf-plan/manifest.ts +8 -0
  250. package/skills/sf-plan-review/body.md +90 -0
  251. package/skills/sf-plan-review/manifest.ts +7 -0
  252. package/skills/sf-receiving-review/body.md +73 -0
  253. package/skills/sf-receiving-review/manifest.ts +7 -0
  254. package/skills/sf-spec-review/body.md +83 -0
  255. package/skills/sf-spec-review/manifest.ts +7 -0
  256. package/skills/sf-tdd/body.md +120 -0
  257. package/skills/sf-tdd/manifest.ts +7 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../../src/core/tasks/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAGrD,SAAS,kBAAkB,CAAC,KAAc;IACxC,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AAC9E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,WAAmB,EACnB,WAAmB;IAEnB,MAAM,QAAQ,GAAG,uBAAuB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAEnE,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QACxF,IAAI,kBAAkB,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QACzC,MAAM,KAAK,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAE1F,OAAO,OAAO,CAAC,GAAG,CAChB,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACtB,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAC1E,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function buildTasksDirectoryPath(projectRoot: string, featureSlug: string): string;
2
+ export declare function buildTaskFilePath(projectRoot: string, featureSlug: string, taskSlug: string): string;
@@ -0,0 +1,11 @@
1
+ import { join } from 'node:path';
2
+ import { assertSafeSlug } from '../slugify.js';
3
+ import { buildFeatureDirectoryPath } from '../specs/paths.js';
4
+ export function buildTasksDirectoryPath(projectRoot, featureSlug) {
5
+ return join(buildFeatureDirectoryPath(projectRoot, featureSlug), 'tasks');
6
+ }
7
+ export function buildTaskFilePath(projectRoot, featureSlug, taskSlug) {
8
+ assertSafeSlug(taskSlug, 'task slug');
9
+ return join(buildTasksDirectoryPath(projectRoot, featureSlug), `${taskSlug}.md`);
10
+ }
11
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../../src/core/tasks/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,MAAM,UAAU,uBAAuB,CAAC,WAAmB,EAAE,WAAmB;IAC9E,OAAO,IAAI,CAAC,yBAAyB,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,WAAmB,EACnB,WAAmB,EACnB,QAAgB;IAEhB,cAAc,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IACtC,OAAO,IAAI,CAAC,uBAAuB,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,GAAG,QAAQ,KAAK,CAAC,CAAC;AACnF,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { TaskStatus } from './types.js';
2
+ export interface UpdateTaskStatusResult {
3
+ featureSlug: string;
4
+ taskSlug: string;
5
+ previousStatus: TaskStatus;
6
+ newStatus: TaskStatus;
7
+ }
8
+ export declare function updateTaskStatus(projectRoot: string, featureSlug: string, taskSlug: string, targetStatus: TaskStatus): Promise<UpdateTaskStatusResult>;
@@ -0,0 +1,30 @@
1
+ import { ScifiError } from '../output/errors.js';
2
+ import { readTaskFile, writeTaskFile } from './frontmatter.js';
3
+ import { buildTaskFilePath } from './paths.js';
4
+ function isMissingPathError(error) {
5
+ return error instanceof Error && 'code' in error && error.code === 'ENOENT';
6
+ }
7
+ export async function updateTaskStatus(projectRoot, featureSlug, taskSlug, targetStatus) {
8
+ const filePath = buildTaskFilePath(projectRoot, featureSlug, taskSlug);
9
+ const file = await readTaskFile(filePath).catch((error) => {
10
+ if (isMissingPathError(error)) {
11
+ throw new ScifiError('NOT_FOUND', `Task "${taskSlug}" does not exist in feature "${featureSlug}".`, { hint: 'Run `scifi task list <slug>` to see available tasks.' });
12
+ }
13
+ throw error;
14
+ });
15
+ if (targetStatus === 'done' && file.frontmatter.status !== 'in-progress') {
16
+ throw new ScifiError('PRECONDITION_FAILED', `Cannot mark task ${taskSlug} as done: task is not in-progress (current status: ${file.frontmatter.status}).`, { hint: 'Start it first with `scifi task start <slug> <task>`.' });
17
+ }
18
+ const previousStatus = file.frontmatter.status;
19
+ await writeTaskFile(filePath, {
20
+ ...file,
21
+ frontmatter: { ...file.frontmatter, status: targetStatus },
22
+ });
23
+ return {
24
+ featureSlug,
25
+ taskSlug,
26
+ previousStatus,
27
+ newStatus: targetStatus,
28
+ };
29
+ }
30
+ //# sourceMappingURL=transition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transition.js","sourceRoot":"","sources":["../../../../src/core/tasks/transition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAU/C,SAAS,kBAAkB,CAAC,KAAc;IACxC,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AAC9E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,WAAmB,EACnB,WAAmB,EACnB,QAAgB,EAChB,YAAwB;IAExB,MAAM,QAAQ,GAAG,iBAAiB,CAAC,WAAW,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;IACvE,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAS,EAAE;QACxE,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,UAAU,CAClB,WAAW,EACX,SAAS,QAAQ,gCAAgC,WAAW,IAAI,EAChE,EAAE,IAAI,EAAE,sDAAsD,EAAE,CACjE,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,IAAI,YAAY,KAAK,MAAM,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QACzE,MAAM,IAAI,UAAU,CAClB,qBAAqB,EACrB,oBAAoB,QAAQ,sDAAsD,IAAI,CAAC,WAAW,CAAC,MAAM,IAAI,EAC7G,EAAE,IAAI,EAAE,uDAAuD,EAAE,CAClE,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;IAE/C,MAAM,aAAa,CAAC,QAAQ,EAAE;QAC5B,GAAG,IAAI;QACP,WAAW,EAAE,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE;KAC3D,CAAC,CAAC;IAEH,OAAO;QACL,WAAW;QACX,QAAQ;QACR,cAAc;QACd,SAAS,EAAE,YAAY;KACxB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ export declare const TASK_STATUS_VALUES: readonly ["pending", "in-progress", "done"];
2
+ export type TaskStatus = (typeof TASK_STATUS_VALUES)[number];
3
+ export interface TaskFrontmatter {
4
+ id: string;
5
+ slug: string;
6
+ status: TaskStatus;
7
+ dependsOn: string[];
8
+ }
@@ -0,0 +1,2 @@
1
+ export const TASK_STATUS_VALUES = ['pending', 'in-progress', 'done'];
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/core/tasks/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,SAAS,EAAE,aAAa,EAAE,MAAM,CAAU,CAAC"}
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@kamilmarzynski/scifi",
3
+ "version": "0.1.1",
4
+ "description": "Specification-driven CLI scaffolding for agentic workflows",
5
+ "type": "module",
6
+ "bin": {
7
+ "scifi": "./dist/src/cli/index.js"
8
+ },
9
+ "exports": {
10
+ ".": "./dist/src/cli/index.js",
11
+ "./skill-types": {
12
+ "types": "./dist/src/core/skills/types.d.ts",
13
+ "default": "./dist/src/core/skills/types.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "skills"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc -p tsconfig.json && node -e \"const fs=require('node:fs'); const path='dist/src/cli/index.js'; const shebang='#!/usr/bin/env node\\n'; const contents=fs.readFileSync(path, 'utf8'); if (!contents.startsWith(shebang)) { fs.writeFileSync(path, shebang + contents); } fs.chmodSync(path, 0o755);\"",
22
+ "prepack": "npm run build && node -e \"require('node:fs').accessSync('dist/src/cli/index.js')\"",
23
+ "typecheck": "tsc --noEmit -p tsconfig.json",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest",
26
+ "coverage": "vitest run --coverage",
27
+ "lint": "biome lint .",
28
+ "lint:fix": "biome lint --write .",
29
+ "format": "biome format --write .",
30
+ "check": "biome check .",
31
+ "check:fix": "biome check --write ."
32
+ },
33
+ "keywords": [
34
+ "cli",
35
+ "scaffolding",
36
+ "specification",
37
+ "typescript"
38
+ ],
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/KamilMarzynski/sci-fi.git"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/KamilMarzynski/sci-fi/issues"
45
+ },
46
+ "homepage": "https://github.com/KamilMarzynski/sci-fi",
47
+ "engines": {
48
+ "node": ">=22.0.0"
49
+ },
50
+ "license": "MIT",
51
+ "publishConfig": {
52
+ "access": "public",
53
+ "provenance": true
54
+ },
55
+ "dependencies": {
56
+ "commander": "^15.0.0",
57
+ "yaml": "^2.9.0",
58
+ "zod": "^4.4.3"
59
+ },
60
+ "devDependencies": {
61
+ "@biomejs/biome": "2.4.16",
62
+ "@types/node": "^24.0.0",
63
+ "@vitest/coverage-v8": "^4.0.0",
64
+ "typescript": "^5.9.0",
65
+ "vitest": "^4.0.0"
66
+ }
67
+ }
@@ -0,0 +1,22 @@
1
+ # Dispatch template: bug-fix code review
2
+
3
+ Dispatch a subagent to review the bug fix before it is accepted. Replace
4
+ `{COMMIT_RANGE}` with the commit(s) the fix produced (a SHA or `<base>..HEAD`)
5
+ and `{CHANGE_BRIEF}` with the root cause and the solution the user agreed to, in
6
+ a sentence or two. There is no feature directory and no task file — this is a
7
+ **fix-mode** review. The criteria and output format live in the `sf-code-review`
8
+ skill — do not restate them here.
9
+
10
+ ```
11
+ You are reviewing a bug fix. Load and follow the `sf-code-review` skill.
12
+
13
+ This is a CODE review in FIX MODE (no task file, no feature directory).
14
+ Changes to review: {COMMIT_RANGE}
15
+ Agreed change (the contract): {CHANGE_BRIEF}
16
+
17
+ Judge the change against that brief: it implements the agreed solution and only
18
+ that, a regression test reproduces the bug and now guards it, and no
19
+ non-negotiable is triggered. Use docs/scifi/CONTEXT.md for glossary context if
20
+ it exists. Return your report and verdict exactly as the sf-code-review skill
21
+ defines.
22
+ ```
@@ -0,0 +1,117 @@
1
+ # sf-bug
2
+
3
+ You run down ONE bug with the user and drive it to a fix. The session has two
4
+ halves: first you investigate *together* until the cause is understood and a fix
5
+ is chosen, then you implement that fix test-first under review. This skill is
6
+ rigid about the seam between them — you do not start fixing until the user has
7
+ agreed on which solution to build.
8
+
9
+ There is no spec and no tracked artifact for a bug. A bug is not a feature: its
10
+ solution emerges from diagnosis, not design. Keep the work in code and tests,
11
+ not in documents.
12
+
13
+ ## The Iron Law
14
+
15
+ ```
16
+ INVESTIGATE → REPORT → AGREE → FIX. NEVER FIX BEFORE THE USER AGREES.
17
+ ```
18
+
19
+ The user reported a bug, not a fix. Your job in the first half is to understand
20
+ it well enough to lay out real options; the choice between them is theirs.
21
+
22
+ ## Flow
23
+
24
+ ### 1. Capture the report
25
+
26
+ - Pin the symptom in the user's words: the error text *verbatim*, the wrong
27
+ output, the failing case. Quote it; do not paraphrase.
28
+ - Note the conditions they hit it under — environment, data, version, steps.
29
+
30
+ Before investigating, create an isolated workspace from the default branch
31
+ (shown as `main` below — substitute your repo's default branch if it differs):
32
+
33
+ ```
34
+ git worktree add -b fix/<slug> .worktrees/fix-<slug> main
35
+ ```
36
+
37
+ Derive `<slug>` from the bug (e.g. `stale-token-refresh`). Work inside it; open
38
+ the PR from it. A bug is untracked, so there is no `scifi` pointer to record.
39
+
40
+ ### 2. Investigate
41
+
42
+ Reproduce, then find the root cause. One hypothesis at a time.
43
+
44
+ - **Reproduce** by the smallest path you can. If you cannot make it happen on
45
+ demand, say so and gather more from the user — an unreproducible bug is not
46
+ ready to fix.
47
+ - **Diagnose**: state a single hypothesis about the cause in one sentence,
48
+ confirm or kill it by *reading the code* and adding observation (a log, a
49
+ probe), not by editing a fix and watching the symptom move. When a hypothesis
50
+ is wrong, record what you learned and form the next.
51
+ - You have the root cause when you can trace the full chain from trigger to
52
+ symptom and point at the line that is wrong and say why.
53
+
54
+ Investigate openly with the user — share what you find as you find it. This is a
55
+ debugging session, not a silent report you deliver at the end.
56
+
57
+ ### 3. Report and propose (the gate)
58
+
59
+ Stop and bring it back to the user. Present:
60
+
61
+ - **The issue** — the root cause in plain language: what is actually wrong and
62
+ why it produces the symptom. Not the symptom restated.
63
+ - **A few solutions** — typically two or three. For each: what it changes, the
64
+ trade-off, and the blast radius. Be honest about a quick patch vs. a deeper
65
+ fix that removes the cause for good. Recommend one and say why.
66
+
67
+ Then debug it together. The user may push back, add context, or reframe the
68
+ problem — fold that in and re-propose. Do not move on until the user has chosen
69
+ a solution. If diagnosis turns up that this is really a missing feature or a
70
+ design decision, say so and stop — that belongs in `sf-feature`, not here.
71
+
72
+ ### 4. Fix, test-first under review
73
+
74
+ Once the user has chosen, implement that solution — and only that solution.
75
+
76
+ - **Hold `sf-tdd`.** The bug becomes its first failing test: write a test that
77
+ reproduces it through the public interface at the smallest scope that captures
78
+ it, watch it fail for the *right* reason (the root cause from step 2), then
79
+ make it pass with the minimal change at the cause. Keep the full suite green.
80
+ - **Review gate.** Dispatch a code-review subagent with `DISPATCH-CODE-REVIEW.md`
81
+ (ships beside this skill) — a fix-mode review: you pass the **change brief**
82
+ (the root cause and the agreed solution), not a task file. Act on its report
83
+ under `sf-receiving-review` with **review type: code**. Re-review until the
84
+ verdict is **Pass** or **With fixes**; a **Fail** re-loops. On **With fixes**,
85
+ address the Minor items (or defer them with the user's ok) before accepting.
86
+ Do not skip it and do not review your own fix.
87
+
88
+ The regression test is the point: it proves the bug existed and guards its
89
+ return.
90
+
91
+ ## When you are stuck
92
+
93
+ | Problem | Move |
94
+ | --- | --- |
95
+ | Can't reproduce | Shrink the variables: pin env, data, version one at a time. |
96
+ | Many possible causes | Bisect — halve the suspect surface each step, don't scan it. |
97
+ | Symptom moves when you touch it | You patched downstream of the cause. Go upstream. |
98
+ | User picks a patch over the real fix | Build it — but record the leftover cause as known debt. |
99
+
100
+ ## Done
101
+
102
+ The bug is done when:
103
+
104
+ - you can state the root cause in one sentence,
105
+ - the user agreed on the solution you built,
106
+ - a test reproduces the bug, which you watched fail then pass,
107
+ - the code review cleared (**Pass**, or **With fixes** with its Minor items
108
+ handled) and the full suite is green.
109
+
110
+ ## Hard rules
111
+
112
+ - Never start fixing before the user has chosen a solution.
113
+ - Never present the symptom as the cause.
114
+ - Never ship a fix with no failing test behind it.
115
+ - Never catch or silence the error in place of removing its cause.
116
+ - Never mark done before the code review clears (**Pass**, or **With fixes** with
117
+ its Minor items handled).
@@ -0,0 +1,8 @@
1
+ import type { SkillManifest } from "scifi/skill-types";
2
+
3
+ export const manifest: SkillManifest = {
4
+ id: "sf-bug",
5
+ description:
6
+ "Investigate one bug with the user, agree on a solution, then fix it test-first under review. No spec or tracked artifact.",
7
+ argumentHint: "[description]",
8
+ };
@@ -0,0 +1,178 @@
1
+ # sf-change
2
+
3
+ The scope of ONE existing feature has changed. Your job is to absorb that
4
+ change cleanly: figure out where the feature currently sits, work out how deep
5
+ the change cuts, update the affected artifacts under the same grilling and
6
+ review gates that produced them, and reset the feature's lifecycle status so it
7
+ never sits *ahead* of the artifacts that back it.
8
+
9
+ You do not re-invent the pipeline. `sf-feature`, `sf-plan`, and `sf-implement`
10
+ already own spec creation, planning, and implementation — each with its own
11
+ grill and its own review gate. Your job is to decide **how far back the change
12
+ rolls the feature**, reset the status to that point, and re-enter the pipeline
13
+ there so the right gate runs again. A change that touches the *what* is not the
14
+ same as one that touches the *how*; getting that judgement right is the work.
15
+
16
+ ## The Iron Law
17
+
18
+ ```
19
+ IDENTIFY → ASSESS STATE → SCOPE THE CHANGE → DECIDE BLAST RADIUS →
20
+ RESET STATUS → RE-ENTER THE PIPELINE.
21
+ NEVER LET STATUS RUN AHEAD OF THE ARTIFACTS.
22
+ ```
23
+
24
+ ## Flow
25
+
26
+ ### 1. Identify the feature
27
+
28
+ The change must attach to one feature. Resolve it before anything else.
29
+
30
+ - `/sf-change <slug>` — treat the argument as an exact feature slug.
31
+ - `/sf-change <description>` — you were given prose, not a slug. Discover
32
+ candidates from **both** `scifi list --json` and `git worktree list` (an
33
+ in-flight feature lives on its own `feat/<slug>` branch and will not appear in
34
+ `scifi list` from the default checkout). Match by slug and title. Present your
35
+ best match (or the candidates, if ambiguous) and **confirm the pick with the
36
+ user**. Never guess silently.
37
+ - **Locate the feature's workspace, then confirm it exists.** Run
38
+ `git worktree list`; if `.worktrees/feat-<slug>` (branch `feat/<slug>`) exists,
39
+ enter it and confirm with `scifi status <slug>` from there. Only when *no*
40
+ matching worktree exists **and** `scifi status <slug>` returns `NOT_FOUND` from
41
+ a checkout that would contain it is the feature truly absent — then stop: for
42
+ genuinely new work point the user at `sf-feature`; for a defect rather than a
43
+ scope change, `sf-fix`. A `NOT_FOUND` while a worktree exists just means you
44
+ are in the wrong checkout, not that the feature is gone.
45
+
46
+ ### 2. Assess current state
47
+
48
+ Read where the feature is before touching anything:
49
+
50
+ ```
51
+ scifi status <slug> --json
52
+ ```
53
+
54
+ Read `status`, the `artifacts` inventory (`artifacts.spec`, `artifacts.design`,
55
+ `artifacts.taskCount`), the per-task statuses in `tasks[]`, and `fixes[]` (each
56
+ carries its own `status`, so filter for the open ones). Then read the artifacts
57
+ themselves —
58
+ `<path>/spec.md`, `<path>/design.md`, the task files under `<path>/tasks/` — and
59
+ grep `docs/scifi/adr/` for decisions touching the area. `<path>` is
60
+ `docs/scifi/specs/<slug>/`. You cannot scope a change without knowing the
61
+ current contract.
62
+
63
+ Enter the feature's worktree (the `worktree` field, fallback
64
+ `.worktrees/feat-<slug>`) before editing any artifact. If the feature was `done`
65
+ and its worktree was cleaned up, recreate one off the default branch
66
+ (`git worktree add -b feat/<slug> .worktrees/feat-<slug> main`, substituting your
67
+ default branch for `main` if it differs) and re-record it
68
+ with `scifi worktree set <slug> --branch feat/<slug> --path
69
+ .worktrees/feat-<slug>`.
70
+
71
+ ### 3. Scope the change (grill it)
72
+
73
+ Interrogate the change the same way `sf-feature` interrogates a new idea — one
74
+ question at a time, concrete either/or where possible. Drive toward:
75
+
76
+ - **What actually changes** — a requirement, an acceptance criterion, something
77
+ moving in or out of scope, or only an internal mechanism.
78
+ - **Why now**, and what the change must *not* break.
79
+ - **Confront it against the existing artifacts.** Does it contradict the spec's
80
+ stated scope? A recorded ADR? The agreed design? Surface the conflict; do not
81
+ paper over it.
82
+
83
+ You are ready to act when you can state, in one sentence, exactly what changes
84
+ and which artifact owns it.
85
+
86
+ ### 4. Decide the blast radius
87
+
88
+ Classify the change by the deepest artifact it invalidates. This decides how far
89
+ back the feature rolls.
90
+
91
+ | The change touches… | Deepest artifact | Roll back to | Re-enter via |
92
+ | --- | --- | --- | --- |
93
+ | The **what** — a requirement, acceptance criterion, in/out-of-scope line | `spec.md` | `spec-ready` | `sf-feature`'s grill + `sf-spec-review`, then `sf-plan`, then `sf-implement` |
94
+ | The **how** — module shape, seams, task breakdown; spec still holds | `design.md` / tasks | `plan-ready` | `sf-plan`'s grill + `sf-plan-review`, then `sf-implement` |
95
+ | A single in-flight task, within the current design | one task file | stay | `sf-implement` (or finish the task in place) |
96
+
97
+ When in doubt, roll back further, not less. A spec change that you treat as a
98
+ mere task tweak leaves the design and code quietly contradicting the contract —
99
+ exactly the failure this skill exists to prevent.
100
+
101
+ ### 5. Reset the status
102
+
103
+ Lower the lifecycle status to the rollback point from step 4, using the CLI
104
+ levers — never by hand-editing metadata:
105
+
106
+ - **Spec-level** → edit `spec.md` first (or re-enter `sf-feature`'s grill for
107
+ the affected sections), then `scifi spec-ready <slug>`. This re-anchors the
108
+ feature at the spec stage; design, tasks, and code are now downstream of a
109
+ changed contract and must be brought back in line.
110
+ - **Design-level** → update `design.md` and the task files, then
111
+ `scifi plan-ready <slug>`.
112
+ - **Task-level** → no status change. `in-progress` stays `in-progress`.
113
+
114
+ Note the one-way gate: `in-progress` is only reachable *from* `plan-ready` via
115
+ `scifi start <slug>`. So a feature you rolled back to `spec-ready` climbs back
116
+ the normal way — `spec-ready → plan-ready → in-progress` — as each gate passes.
117
+ You do not get to jump straight back to where it was.
118
+
119
+ **If the feature was `done`,** a spec- or design-level change reopens it: reset
120
+ the status as above and the feature re-enters the pipeline. Do not leave a `done`
121
+ feature whose contract has changed.
122
+
123
+ ### 6. Re-enter the pipeline
124
+
125
+ Hand control to the owning skill for the rollback point — that is where the
126
+ grill and the review gate live:
127
+
128
+ - Rolled to `spec-ready` → run `sf-feature` to settle the changed spec under
129
+ `sf-spec-review`, then `sf-plan`, then `sf-implement`. Each gate re-runs.
130
+ `sf-feature` reopens the *existing* container in this case — it skips its
131
+ `scifi spec` create step, so you keep working against the original feature
132
+ rather than scaffolding a new one.
133
+ - Rolled to `plan-ready` → run `sf-plan` to settle the changed design and tasks
134
+ under `sf-plan-review`, then `sf-implement`.
135
+ - Stayed `in-progress` → run `sf-implement`; it resumes from the next runnable
136
+ task and picks up the added or removed task files.
137
+
138
+ Adding or removing a task means adding or deleting a file under `<path>/tasks/`
139
+ (`sf-plan` owns the task template and the decomposition rules — re-enter it
140
+ rather than hand-rolling task files for anything beyond a trivial edit). The CLI
141
+ tracks task *status* (`scifi task start|done`), not task creation.
142
+
143
+ **Re-open the tasks the change invalidated.** A design change usually rewrites
144
+ tasks that are already `done`, and `sf-implement` *skips* `done` tasks on resume
145
+ — so a changed-but-done task would never be rebuilt. For each existing task the
146
+ change invalidates, run `scifi task start <slug> <task>` to flip it back to
147
+ `in-progress` before re-entering `sf-implement`; it then picks the task up as
148
+ runnable. A brand-new task file already starts `pending`, so it needs no reset.
149
+
150
+ ## When you are stuck
151
+
152
+ | Problem | Move |
153
+ | --- | --- |
154
+ | Unsure if it's a spec or design change | Ask: does an *acceptance criterion* move? Yes → spec. No → design. |
155
+ | Change contradicts a recorded ADR | Surface it. The decision may need revisiting before the change lands. |
156
+ | Feature is `done` and change is large | Reopen via `spec-ready`; treat it as a near-fresh pass through the pipeline. |
157
+ | Change turns out to be a defect, not new scope | Stop. Route to `sf-fix`. |
158
+ | Change is really a brand-new feature | Stop. Route to `sf-feature`; don't overload this one. |
159
+
160
+ ## Done
161
+
162
+ The change is absorbed when:
163
+
164
+ - the target feature is identified and its current state read,
165
+ - the change is scoped to exactly the artifact it invalidates,
166
+ - that artifact is updated under its own grill and review gate,
167
+ - the lifecycle status is reset to match — never ahead of — the artifacts,
168
+ - and the feature has re-entered the pipeline at the rollback point.
169
+
170
+ ## Hard rules
171
+
172
+ - Never change scope silently — grill it and confront it against the artifacts.
173
+ - Never lower a feature past `spec-ready`/`plan-ready` without re-running that
174
+ stage's review gate.
175
+ - Never leave the status ahead of the artifacts: a changed spec means the
176
+ feature is no longer `done`.
177
+ - Never hand-edit lifecycle metadata — use `scifi spec-ready` / `plan-ready`.
178
+ - Never absorb a defect or a new feature here — route to `sf-fix` / `sf-feature`.
@@ -0,0 +1,8 @@
1
+ import type { SkillManifest } from "scifi/skill-types";
2
+
3
+ export const manifest: SkillManifest = {
4
+ id: "sf-change",
5
+ description:
6
+ "Absorb a scope change to an existing feature: scope it against spec/design, reset lifecycle status to the deepest artifact it invalidates, and re-enter the pipeline so the right review gate runs again.",
7
+ argumentHint: "[feature-slug | description]",
8
+ };
@@ -0,0 +1,155 @@
1
+ # sf-code-review
2
+
3
+ You are a critic. You were dispatched to review ONE change before it is accepted
4
+ — a planned feature task, or a defect fix. You do not write the code and you do
5
+ not fix it. You read, you judge, you report back to the agent that dispatched
6
+ you.
7
+
8
+ You review by reading only. You do not run the suite or the build — the final
9
+ `sf-handover` subagent owns that. Trust the diff and the files in front of
10
+ you.
11
+
12
+ ## Inputs
13
+
14
+ You review in one of two modes; the dispatcher tells you which by what it hands
15
+ you.
16
+
17
+ **Task mode** — dispatched by `sf-implement` to gate one task of a planned
18
+ feature. You get:
19
+
20
+ - `{COMMIT_RANGE}` — the commit(s) the implementer produced (a SHA, or
21
+ `<base>..HEAD`).
22
+ - `{FEATURE_PATH}` — the feature directory (e.g. `docs/scifi/specs/<slug>`).
23
+ - `{TASK_SLUG}` — which task under that feature this change implements.
24
+
25
+ **Fix mode** — dispatched by `sf-bug` or `sf-fix` to gate a defect fix. There is
26
+ no task file, and for an untracked bug no feature directory either. You get:
27
+
28
+ - `{COMMIT_RANGE}` — the commit(s) the fix produced.
29
+ - `{CHANGE_BRIEF}` — the root cause and the solution the user agreed to, in a
30
+ sentence or two. This is the contract the change must match.
31
+ - `{FEATURE_PATH}` *(sf-fix only)* — the owning feature directory, so you can
32
+ judge the fix against its `spec.md` / `design.md` as original intent.
33
+
34
+ In either mode, if `{COMMIT_RANGE}` is empty you cannot see the change — say so
35
+ and stop. In task mode, if `{FEATURE_PATH}/design.md` is missing you have no
36
+ contract to judge against — say so and stop. In fix mode there is no design
37
+ contract; the `{CHANGE_BRIEF}` is the contract.
38
+
39
+ ## What to read
40
+
41
+ - The diff — `git show {COMMIT_RANGE}` or `git diff {COMMIT_RANGE}`. Then read
42
+ the touched files in full where the diff alone hides context.
43
+ - **Task mode:** `{FEATURE_PATH}/design.md` (the technical contract) and the task
44
+ file under `{FEATURE_PATH}/tasks/` for `{TASK_SLUG}` — its **Tests first**,
45
+ **Acceptance**, and **Validation** sections.
46
+ - **Fix mode:** the `{CHANGE_BRIEF}`. For `sf-fix`, also `{FEATURE_PATH}/spec.md`
47
+ and `design.md` — the original intent the fix must restore, not contradict.
48
+ - `docs/scifi/CONTEXT.md` — the project's ubiquitous language (canonical
49
+ glossary of domain terms), if it exists.
50
+
51
+ Read everything that applies to your mode before judging. Never invent project
52
+ facts; if something is unknowable from these files, flag it as a question
53
+ instead of assuming.
54
+
55
+ ## What to check
56
+
57
+ Go through the diff against this checklist. No fixed priority order — weigh each
58
+ finding by its real impact on the change.
59
+
60
+ - **Acceptance & design** — the change does what it was dispatched to do, and no
61
+ more. *Task mode:* it satisfies the task's acceptance criteria and matches
62
+ `design.md`; an acceptance item with no implementation, or code that
63
+ contradicts the design, is a defect. *Fix mode:* it implements the agreed
64
+ solution from `{CHANGE_BRIEF}` and only that — and, for `sf-fix`, does not
65
+ reintroduce a deviation from the feature's `spec.md` / `design.md`. Behavior
66
+ outside the dispatched scope is a defect in either mode — flag scope creep.
67
+ - **TDD evidence** — the behavior is covered by a test written first. *Task
68
+ mode:* every behavior in the task's **Tests first** has a test. *Fix mode:* a
69
+ regression test reproduces the defect and now guards it. In both, the tests
70
+ exercise the change through its public interface, not by mocking the module
71
+ under test, and assert observable behavior, not shape. Production code with no
72
+ test behind it is a defect (see non-negotiables).
73
+ - **Deep modules** — real behavior behind a narrow interface. Apply the deletion
74
+ test: would removing a unit *concentrate* complexity (keep it) or just
75
+ *scatter* it (inline it)? Flag shallow seams — pass-through wrappers, a class
76
+ that only forwards calls, a "utils" dumping ground, a function extracted solely
77
+ so a test can reach it.
78
+ - **Seam declaration** — new seams (a new boundary, dependency, or
79
+ communication pattern). *Task mode:* they must be declared in `design.md`, not
80
+ introduced silently; judge against the design, not an external architecture
81
+ doc. *Fix mode:* a fix that quietly cuts a new seam is a smell — flag it; it
82
+ usually signals the change outgrew a fix and belongs in a feature.
83
+ - **Simplification (code judo)** — is there a reframing that deletes a whole
84
+ branch, mode, flag, or conditional rather than adding to it? Flag incidental
85
+ complexity the change preserves when a clearly simpler shape exists. Do not
86
+ invent hypothetical rewrites — only call a simplification that is concrete and
87
+ visible.
88
+ - **Spaghetti & types** — new conditionals bolted onto unrelated flows that make
89
+ surrounding code harder to reason about; unjustified casts, optionality, or
90
+ loosely-shaped objects that obscure the real invariant. Prefer explicit typed
91
+ boundaries.
92
+ - **Naming / glossary** — domain terms used in the code or tests that are not in
93
+ `CONTEXT.md` (ubiquitous language) and were not proposed for it. A
94
+ naming-consistency check, not structural.
95
+ - **Placeholders** — any `TBD` / `TODO` / stubbed body / empty implementation
96
+ presented as finished work.
97
+
98
+ ## Non-negotiables
99
+
100
+ These are not judgment calls. When the diff contains one, assign at least the
101
+ stated severity regardless of how small it looks. Generalize each rule to the
102
+ project's language — e.g. "type escape" is `any`/`unknown`/an unchecked cast in
103
+ TypeScript, an `interface{}` assertion in Go, a `# type: ignore` in Python.
104
+
105
+ - **Type escape or cast without a justifying comment** → Critical.
106
+ - **Production code with no failing-test origin** (no test exercises it) →
107
+ Critical.
108
+ - **Silent failure** — a swallowed error, an empty catch, a discarded result, a
109
+ fallback that hides a broken invariant → Critical.
110
+ - **Changed public behavior with no doc update** in the same change → Important.
111
+
112
+ ## How to report
113
+
114
+ Open with a header that names what this is, so the receiving agent applies the
115
+ right lens: **`Code review of <subject>`** — the task slug in task mode, or a
116
+ short label for the change in fix mode. Then use this exact shape:
117
+
118
+ ```
119
+ # Code review of <subject>
120
+
121
+ ### Strengths
122
+ - <what the change gets right — be specific; accurate praise earns trust>
123
+
124
+ ### Issues
125
+
126
+ #### Critical (must fix)
127
+ - Where: <file:line / quoted diff hunk>
128
+ Problem: <what is wrong>
129
+ Fix: <concrete change, or the exact question to ask the user>
130
+
131
+ #### Important (should fix)
132
+ - ...
133
+
134
+ #### Minor (nice to have)
135
+ - ...
136
+
137
+ ### Verdict: Pass | Fail | With fixes
138
+ <one-line technical reason>
139
+ ```
140
+
141
+ Calibration:
142
+
143
+ - **Pass** — acceptance (or the agreed fix) met, design matched where one
144
+ exists, tests cover every behavior (or a regression test guards the defect),
145
+ modules are deep, new seams declared, no non-negotiable triggered, no
146
+ placeholders. No Critical or Important issues.
147
+ - **With fixes** — only Minor issues remain; the change is sound enough to land
148
+ once they are addressed.
149
+ - **Fail** — any Critical or Important issue. An uncovered acceptance item,
150
+ untested production code, a triggered non-negotiable, an undeclared new seam,
151
+ or a placeholder is always at least Important.
152
+
153
+ Be specific — quote the line, name the file. The receiving agent acts on your
154
+ list directly, so vague feedback wastes a round trip. Do not mark nitpicks as
155
+ Critical. Do not edit any file yourself.
@@ -0,0 +1,7 @@
1
+ import type { SkillManifest } from "scifi/skill-types";
2
+
3
+ export const manifest: SkillManifest = {
4
+ id: "sf-code-review",
5
+ description:
6
+ "Read-only critic pass on one change: a feature task against design.md + the task's acceptance, or a bug/fix against the agreed solution. Returns a verdict that gates acceptance.",
7
+ };