hammerkit 1.6.0 → 1.8.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 (277) hide show
  1. package/README.md +7 -7
  2. package/build.schema.json +59 -15
  3. package/dist/cache/backends/local-cache-backend.js +58 -1
  4. package/dist/cache/backends/registry/registry-client.js +264 -0
  5. package/dist/cache/backends/registry/registry-credentials.js +81 -0
  6. package/dist/cache/backends/registry/registry-reference.js +36 -0
  7. package/dist/cache/backends/registry-cache-backend.js +145 -0
  8. package/dist/cache/backends/s3-cache-backend.js +99 -46
  9. package/dist/cache/cache-backend-registry.js +2 -0
  10. package/dist/cache/cache-inventory.js +84 -0
  11. package/dist/cache/cache-sync.js +77 -0
  12. package/dist/cache/explain.js +214 -0
  13. package/dist/cache/last-resolved.js +48 -0
  14. package/dist/cache/read-only.js +15 -0
  15. package/dist/cache/retention.js +61 -0
  16. package/dist/cli.js +43 -0
  17. package/dist/docker/using-container.js +17 -1
  18. package/dist/executer/archive-task-entry.js +21 -0
  19. package/dist/executer/await-completed-dependencies.js +32 -14
  20. package/dist/executer/cache-metadata.js +9 -2
  21. package/dist/executer/docker-service.js +2 -1
  22. package/dist/executer/docker-task.js +18 -3
  23. package/dist/executer/dry-run.js +73 -0
  24. package/dist/executer/event-cache.js +6 -5
  25. package/dist/executer/execute-command.js +21 -3
  26. package/dist/executer/execute-docker.js +18 -2
  27. package/dist/executer/execute-work-service.js +10 -1
  28. package/dist/executer/execute-work-task.js +111 -26
  29. package/dist/executer/execute-work-tree.js +26 -1
  30. package/dist/executer/execution-steps.js +3 -8
  31. package/dist/executer/get-container-binds.js +13 -1
  32. package/dist/executer/get-scheduler-execute-result.js +4 -1
  33. package/dist/executer/lazy-resolver.js +15 -3
  34. package/dist/executer/reset-work-tree.js +2 -0
  35. package/dist/executer/run-summary.js +102 -0
  36. package/dist/executer/scheduler/enqueue-next.js +15 -60
  37. package/dist/executer/scheduler/state-key.js +78 -0
  38. package/dist/index.js +14 -1
  39. package/dist/kubernetes/apply.js +6 -7
  40. package/dist/kubernetes/await-running-state.js +100 -60
  41. package/dist/kubernetes/ensure-http-route.js +7 -10
  42. package/dist/kubernetes/ensure-namespace.js +9 -10
  43. package/dist/kubernetes/ensure-persistent-data.js +6 -5
  44. package/dist/kubernetes/get-service-ip.js +2 -2
  45. package/dist/kubernetes/kubernetes-instance.js +0 -4
  46. package/dist/kubernetes/remove-persistent-data.js +16 -6
  47. package/dist/kubernetes/resolve-pod-name.js +7 -7
  48. package/dist/log.js +16 -6
  49. package/dist/logging/live-logger.js +2 -1
  50. package/dist/optimizer/calculate-checksum.js +8 -2
  51. package/dist/optimizer/get-work-cache-stats.js +66 -16
  52. package/dist/optimizer/work-task-cache-description.js +34 -5
  53. package/dist/planner/project-root.js +26 -0
  54. package/dist/planner/utils/append-work-environment.js +1 -1
  55. package/dist/planner/utils/append-work-service.js +2 -1
  56. package/dist/planner/utils/append-work-task.js +7 -1
  57. package/dist/planner/utils/get-outputs-to-reset.js +20 -0
  58. package/dist/planner/utils/parse-work-generate.js +2 -2
  59. package/dist/planner/utils/parse-work-source.js +35 -30
  60. package/dist/planner/utils/portable-path.js +30 -0
  61. package/dist/planner/work-cache-id.js +2 -2
  62. package/dist/planner/work-graph-serializer.js +103 -0
  63. package/dist/planner/work-instance-id.js +15 -0
  64. package/dist/planner/work-runtime-docker.js +58 -15
  65. package/dist/planner/work-runtime-kubernetes.js +95 -45
  66. package/dist/planner/work-runtime-local.js +31 -8
  67. package/dist/planner/work-service-id.js +16 -4
  68. package/dist/program.js +235 -1
  69. package/dist/schema/build-file-base-task-schema.js +4 -0
  70. package/dist/schema/build-file-environment-schema-http-route.js +15 -0
  71. package/dist/schema/build-file-environment-schema.js +8 -2
  72. package/dist/schema/build-file-schema.js +1 -1
  73. package/dist/schema/cache-schema.js +21 -2
  74. package/dist/schema/duration-schema.js +18 -0
  75. package/dist/schema/envs-schema.js +1 -1
  76. package/dist/schema/reference-parser.js +3 -0
  77. package/dist/schema/size-schema.js +18 -0
  78. package/dist/schema/work-scope-parser.js +15 -2
  79. package/dist/utils/units.js +87 -0
  80. package/package.json +20 -9
  81. package/.agents/FEEDBACK.md +0 -74
  82. package/.agents/skills/before-commit/SKILL.md +0 -101
  83. package/.agents/skills/do-the-work/SKILL.md +0 -64
  84. package/.agents/skills/fix-ci/SKILL.md +0 -98
  85. package/.agents/skills/speckit-git-commit/SKILL.md +0 -53
  86. package/.agents/skills/speckit-git-feature/SKILL.md +0 -72
  87. package/.agents/skills/speckit-git-initialize/SKILL.md +0 -54
  88. package/.agents/skills/speckit-git-remote/SKILL.md +0 -50
  89. package/.agents/skills/speckit-git-validate/SKILL.md +0 -54
  90. package/.devcontainer/Dockerfile +0 -5
  91. package/.devcontainer/devcontainer.json +0 -40
  92. package/.gitbook.yaml +0 -5
  93. package/.specify/extensions/.registry +0 -37
  94. package/.specify/extensions/git/README.md +0 -100
  95. package/.specify/extensions/git/commands/speckit.git.commit.md +0 -48
  96. package/.specify/extensions/git/commands/speckit.git.feature.md +0 -67
  97. package/.specify/extensions/git/commands/speckit.git.initialize.md +0 -49
  98. package/.specify/extensions/git/commands/speckit.git.remote.md +0 -45
  99. package/.specify/extensions/git/commands/speckit.git.validate.md +0 -49
  100. package/.specify/extensions/git/config-template.yml +0 -62
  101. package/.specify/extensions/git/extension.yml +0 -140
  102. package/.specify/extensions/git/git-config.yml +0 -62
  103. package/.specify/extensions.yml +0 -149
  104. package/.specify/feature.json +0 -3
  105. package/.specify/init-options.json +0 -10
  106. package/.specify/integration.json +0 -15
  107. package/.specify/integrations/claude.manifest.json +0 -16
  108. package/.specify/integrations/speckit.manifest.json +0 -17
  109. package/.specify/memory/constitution.md +0 -162
  110. package/.specify/templates/checklist-template.md +0 -40
  111. package/.specify/templates/constitution-template.md +0 -50
  112. package/.specify/templates/plan-template.md +0 -113
  113. package/.specify/templates/spec-template.md +0 -131
  114. package/.specify/templates/tasks-template.md +0 -252
  115. package/.specify/workflows/speckit/workflow.yml +0 -77
  116. package/.specify/workflows/workflow-registry.json +0 -13
  117. package/AGENTS.md +0 -94
  118. package/Dockerfile +0 -32
  119. package/FOLLOWUPS.md +0 -34
  120. package/SUMMARY.md +0 -68
  121. package/VALUES.md +0 -53
  122. package/dist/cache/auto-pull.spec.js +0 -123
  123. package/dist/cache/backends/local-cache-backend.spec.js +0 -72
  124. package/dist/cache/backends/s3-cache-backend.spec.js +0 -118
  125. package/dist/cache/cache-backend-registry.spec.js +0 -22
  126. package/dist/cache/resolve-cache.spec.js +0 -52
  127. package/dist/cli.spec.js +0 -23
  128. package/dist/console/get-logger.spec.js +0 -23
  129. package/dist/docker/get-default-platform.spec.js +0 -29
  130. package/dist/docker/longest-common-prefix.spec.js +0 -26
  131. package/dist/docker/package.spec.js +0 -145
  132. package/dist/docker/remove-container.spec.js +0 -32
  133. package/dist/docker/using-container.spec.js +0 -90
  134. package/dist/environment/merge-environment-variables.spec.js +0 -24
  135. package/dist/environment/replace-env-variables.spec.js +0 -47
  136. package/dist/executer/abort.spec.js +0 -18
  137. package/dist/executer/environment-mock.spec.js +0 -22
  138. package/dist/executer/execute.spec.js +0 -51
  139. package/dist/executer/get-service-containers.spec.js +0 -59
  140. package/dist/executer/get-service-env-hints.spec.js +0 -65
  141. package/dist/executer/label-values.spec.js +0 -61
  142. package/dist/executer/lazy-resolver.spec.js +0 -20
  143. package/dist/executer/process-manager.spec.js +0 -54
  144. package/dist/executer/scheduler/enqueue-next.spec.js +0 -136
  145. package/dist/executer/state-resolver.spec.js +0 -37
  146. package/dist/executer/state.spec.js +0 -66
  147. package/dist/executer/states.spec.js +0 -14
  148. package/dist/file/get-file-context.spec.js +0 -178
  149. package/dist/file/move-files.spec.js +0 -48
  150. package/dist/kubernetes/ensure-http-route.spec.js +0 -70
  151. package/dist/kubernetes/ensure-ingress.js +0 -54
  152. package/dist/kubernetes/ensure-namespace.spec.js +0 -39
  153. package/dist/kubernetes/resolve-pod-name.spec.js +0 -81
  154. package/dist/log.spec.js +0 -131
  155. package/dist/logging/grouped-logger.spec.js +0 -84
  156. package/dist/logging/interactive-logger.spec.js +0 -57
  157. package/dist/logging/live-logger.spec.js +0 -37
  158. package/dist/logging/loggers.spec.js +0 -154
  159. package/dist/optimizer/calculate-checksum.spec.js +0 -34
  160. package/dist/optimizer/get-cache-directory.spec.js +0 -59
  161. package/dist/optimizer/get-work-cache-stats.spec.js +0 -79
  162. package/dist/optimizer/work-task-cache-description.spec.js +0 -45
  163. package/dist/parser/default-build-file.spec.js +0 -48
  164. package/dist/parser/parse-label-arguments.spec.js +0 -27
  165. package/dist/parser/read-build-file.spec.js +0 -89
  166. package/dist/parser/read-env-file.spec.js +0 -42
  167. package/dist/planner/utils/append-work-service.spec.js +0 -71
  168. package/dist/planner/utils/get-container-user.spec.js +0 -30
  169. package/dist/planner/utils/get-default-kube-config.spec.js +0 -11
  170. package/dist/planner/utils/normalize-path.spec.js +0 -24
  171. package/dist/planner/utils/parse-work-command.spec.js +0 -21
  172. package/dist/planner/utils/parse-work-generate.spec.js +0 -45
  173. package/dist/planner/utils/parse-work-mount.spec.js +0 -62
  174. package/dist/planner/utils/parse-work-mounts.spec.js +0 -19
  175. package/dist/planner/utils/parse-work-port.spec.js +0 -24
  176. package/dist/planner/utils/parse-work-ports.spec.js +0 -24
  177. package/dist/planner/utils/parse-work-source.spec.js +0 -58
  178. package/dist/planner/utils/parse-work-volume.spec.js +0 -18
  179. package/dist/planner/utils/plan-work-tasks.spec.js +0 -30
  180. package/dist/planner/utils/plan-work-volume.spec.js +0 -33
  181. package/dist/planner/utils/split-name.spec.js +0 -16
  182. package/dist/planner/utils/template-value.spec.js +0 -14
  183. package/dist/planner/validate.spec.js +0 -251
  184. package/dist/planner/work-cache-id.spec.js +0 -173
  185. package/dist/planner/work-runtime-local.spec.js +0 -193
  186. package/dist/program-commands.spec.js +0 -84
  187. package/dist/program.spec.js +0 -43
  188. package/dist/schema/build-file-environment-schema-ingress.js +0 -14
  189. package/dist/schema/build-file-schema.spec.js +0 -61
  190. package/dist/schema/doc-build-files.spec.js +0 -223
  191. package/dist/schema/parse-error.spec.js +0 -18
  192. package/dist/schema/reference-parser.spec.js +0 -83
  193. package/dist/schema/schema-parser.spec.js +0 -80
  194. package/dist/schema/type-guards.spec.js +0 -23
  195. package/dist/schema/work-scope-parser.spec.js +0 -66
  196. package/dist/service/update-service-status.spec.js +0 -27
  197. package/dist/testing/ensure-local-registry.js +0 -134
  198. package/dist/testing/example-test-suite.js +0 -63
  199. package/dist/testing/expect.js +0 -82
  200. package/dist/testing/get-test-suite.js +0 -9
  201. package/dist/testing/http-get.js +0 -21
  202. package/dist/testing/integration/cache.spec.js +0 -49
  203. package/dist/testing/integration/cancellation.spec.js +0 -45
  204. package/dist/testing/integration/clean.spec.js +0 -43
  205. package/dist/testing/integration/cmd.spec.js +0 -15
  206. package/dist/testing/integration/concurrency.spec.js +0 -19
  207. package/dist/testing/integration/docker-package-registry.spec.js +0 -44
  208. package/dist/testing/integration/docker-service-crash.spec.js +0 -36
  209. package/dist/testing/integration/docker.spec.js +0 -18
  210. package/dist/testing/integration/env.spec.js +0 -48
  211. package/dist/testing/integration/environment.spec.js +0 -97
  212. package/dist/testing/integration/error.spec.js +0 -19
  213. package/dist/testing/integration/export.spec.js +0 -22
  214. package/dist/testing/integration/extend.spec.js +0 -29
  215. package/dist/testing/integration/glob.spec.js +0 -40
  216. package/dist/testing/integration/include.spec.js +0 -31
  217. package/dist/testing/integration/invalid.spec.js +0 -17
  218. package/dist/testing/integration/invalid_loop.spec.js +0 -56
  219. package/dist/testing/integration/k8s-state-cache.spec.js +0 -41
  220. package/dist/testing/integration/kubernetes-service.spec.js +0 -148
  221. package/dist/testing/integration/kubernetes.spec.js +0 -99
  222. package/dist/testing/integration/labels.spec.js +0 -42
  223. package/dist/testing/integration/local.spec.js +0 -14
  224. package/dist/testing/integration/monorepo.spec.js +0 -15
  225. package/dist/testing/integration/multi-line.spec.js +0 -17
  226. package/dist/testing/integration/reference.spec.js +0 -21
  227. package/dist/testing/integration/services.spec.js +0 -38
  228. package/dist/testing/integration/store.spec.js +0 -52
  229. package/dist/testing/integration/unknown.spec.js +0 -28
  230. package/dist/testing/integration/validate.spec.js +0 -160
  231. package/dist/testing/integration/watch-dependency.spec.js +0 -33
  232. package/dist/testing/integration/watch.spec.js +0 -45
  233. package/dist/testing/requires-kubernetes.js +0 -18
  234. package/dist/testing/requires-linux-containers.js +0 -18
  235. package/dist/testing/requires-windows-containers.js +0 -18
  236. package/dist/testing/test-case.js +0 -75
  237. package/dist/testing/test-streams.js +0 -28
  238. package/dist/testing/test-suite-setup.js +0 -3
  239. package/dist/testing/test-suite.js +0 -3
  240. package/dist/testing/testing-timeout.js +0 -32
  241. package/dist/utils/abort-event.spec.js +0 -46
  242. package/dist/utils/buffer-context.spec.js +0 -38
  243. package/dist/utils/ci.spec.js +0 -53
  244. package/dist/utils/debouncer.spec.js +0 -58
  245. package/dist/utils/emitter.spec.js +0 -34
  246. package/dist/utils/empty-writable.spec.js +0 -18
  247. package/dist/utils/environment-config.spec.js +0 -26
  248. package/dist/utils/fail-never.spec.js +0 -9
  249. package/dist/utils/sleep.spec.js +0 -22
  250. package/dist/version.spec.js +0 -12
  251. package/specs/build-file/caches/spec.md +0 -114
  252. package/specs/build-file/environment-variables/spec.md +0 -82
  253. package/specs/build-file/includes/spec.md +0 -60
  254. package/specs/build-file/references/spec.md +0 -56
  255. package/specs/build-file/spec.md +0 -62
  256. package/specs/cli/clean/spec.md +0 -85
  257. package/specs/cli/down/spec.md +0 -85
  258. package/specs/cli/execute/spec.md +0 -101
  259. package/specs/cli/init/spec.md +0 -64
  260. package/specs/cli/ls/spec.md +0 -66
  261. package/specs/cli/package/spec.md +0 -110
  262. package/specs/cli/spec.md +0 -73
  263. package/specs/cli/store-restore/spec.md +0 -74
  264. package/specs/cli/up/spec.md +0 -80
  265. package/specs/cli/validate/spec.md +0 -70
  266. package/specs/labels/spec.md +0 -91
  267. package/specs/service/container/spec.md +0 -120
  268. package/specs/service/kubernetes/spec.md +0 -97
  269. package/specs/service/spec.md +0 -105
  270. package/specs/task/caching/spec.md +0 -107
  271. package/specs/task/container/spec.md +0 -118
  272. package/specs/task/dependencies/spec.md +0 -70
  273. package/specs/task/extending/spec.md +0 -74
  274. package/specs/task/kubernetes/spec.md +0 -125
  275. package/specs/task/needs/spec.md +0 -59
  276. package/specs/task/spec.md +0 -87
  277. package/specs/task/watching/spec.md +0 -69
@@ -1,82 +0,0 @@
1
- # Feature Specification: Build File: Environment Variables
2
-
3
- **Feature Branch**: `001-current-state-specs`
4
-
5
- **Created**: 2026-05-30
6
-
7
- **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
-
9
- **Input**: Existing behavior documented in `docs/build-file/environment-variables.md`
10
-
11
- ## User Scenarios & Testing *(mandatory)*
12
-
13
- ### User Story 1 - Pass shell environment variables into a task (Priority: P1)
14
-
15
- A developer needs a task to use a variable (e.g. `VERSION`) that is set in the shell before running hammerkit. They declare the variable in the task's `envs:` block referencing the shell value (`$VERSION`). Hammerkit validates the variable is set and injects it into the task execution environment.
16
-
17
- **Why this priority**: This is the most common env-var pattern; it prevents silent failures from unset variables.
18
-
19
- **Independent Test**: Export a variable in the shell, declare it in the task `envs:`, run the task, and confirm the command receives the correct value. Then unset the variable and confirm hammerkit throws an error rather than passing an empty value.
20
-
21
- **Acceptance Scenarios**:
22
-
23
- 1. **Given** a shell variable `VERSION=1.0.0` is exported, **When** a task declares `VERSION: $VERSION` in its `envs:` and runs `echo $VERSION`, **Then** the output is `1.0.0`.
24
- 2. **Given** a task declares `VERSION: $VERSION` but the shell variable is not set, **When** the task is invoked, **Then** hammerkit throws an error before executing any commands.
25
-
26
- ### User Story 2 - Use .env file for secret values (Priority: P2)
27
-
28
- A developer stores sensitive credentials (e.g. `NPM_TOKEN`) in a `.env` file that is excluded from version control. The task `envs:` block references the variable using the `$NPM_TOKEN` syntax. Hammerkit reads the `.env` file and makes the value available to the task.
29
-
30
- **Why this priority**: Keeps secrets out of the build file and version history while still being available during task execution.
31
-
32
- **Independent Test**: Create a `.env` file with `NPM_TOKEN=abc`, declare it in the task `envs:`, run the task, and confirm the token is available to the command.
33
-
34
- **Acceptance Scenarios**:
35
-
36
- 1. **Given** a `.env` file containing `NPM_TOKEN=abc`, **When** a task declares `NPM_TOKEN: $NPM_TOKEN` in its `envs:` and is executed, **Then** the command receives `NPM_TOKEN=abc`.
37
-
38
- ### User Story 3 - Define environment variables in the build file (Priority: P2)
39
-
40
- A developer defines default values for environment variables at the top-level `envs:` block or within a specific task's `envs:` block. Task-level declarations override the file-level defaults for that task only.
41
-
42
- **Why this priority**: Centralises configuration that does not change per-developer while allowing per-task overrides.
43
-
44
- **Independent Test**: Declare `NODE_VERSION: 14.16.0` at the top level and `NODE_VERSION: 14.0.0` inside one task's `envs:`. Run each task and verify each receives the correct version string.
45
-
46
- **Acceptance Scenarios**:
47
-
48
- 1. **Given** `NODE_VERSION: 14.16.0` is declared in the top-level `envs:`, **When** a task that does not override it runs `echo $NODE_VERSION`, **Then** the output is `14.16.0`.
49
- 2. **Given** a task declares `NODE_VERSION: 14.0.0` in its own `envs:`, **When** that task runs, **Then** its value (`14.0.0`) takes precedence over the top-level value.
50
- 3. **Given** top-level `envs:` are declared in file A, **When** a task from a referenced or included build file B runs, **Then** it does NOT have access to file A's top-level env vars.
51
-
52
- ### Edge Cases
53
-
54
- - Referencing an undefined shell variable that is not in `.env` causes a hard error before task execution begins.
55
- - Variables declared in a build file's top-level `envs:` are scoped to that file; they do not leak into referenced or included build files.
56
- - The `.env` file is loaded relative to the project directory containing the build file.
57
-
58
- ## Requirements *(mandatory)*
59
-
60
- ### Functional Requirements
61
-
62
- - **FR-001**: The system MUST support declaring environment variables in the top-level `envs:` map of a build file, making them available to all tasks in that file.
63
- - **FR-002**: The system MUST support declaring environment variables in a task-level `envs:` map, overriding any same-named top-level declaration for that task.
64
- - **FR-003**: The system MUST resolve `$VAR` references in `envs:` values from the shell environment at the time the task is executed.
65
- - **FR-004**: The system MUST read environment variable values from a `.env` file in the project directory and make them available for `$VAR` resolution.
66
- - **FR-005**: The system MUST throw an error if an `envs:` entry references a shell or `.env` variable that is not defined, rather than passing an empty or undefined value to the task.
67
- - **FR-006**: Top-level `envs:` declarations MUST be scoped to the build file in which they are defined and MUST NOT be visible to referenced or included build files.
68
-
69
- ## Success Criteria *(mandatory)*
70
-
71
- ### Measurable Outcomes
72
-
73
- - **SC-001**: A task that declares a shell-sourced env var receives the correct value when the variable is set, and hammerkit exits with a non-zero code and an error message when the variable is unset.
74
- - **SC-002**: A task that sources a value from `.env` receives the correct value without the secret appearing in the build file.
75
- - **SC-003**: A task-level `envs:` declaration overrides a file-level declaration for the same variable name within that task only.
76
- - **SC-004**: Tasks in a referenced or included build file do not inherit top-level env vars from the including build file.
77
-
78
- ## Assumptions
79
-
80
- - `.env` file loading is automatic; no explicit declaration in the build file is needed beyond the `$VAR` reference in `envs:`.
81
- - Variables defined as literal values in `envs:` (not using `$VAR` syntax) are always available and require no shell validation.
82
- - The precedence order (most specific wins) is: task-level `envs:` > top-level `envs:` > `.env` file > shell environment, though the docs do not enumerate this explicitly; this assumption is recorded here for review.
@@ -1,60 +0,0 @@
1
- # Feature Specification: Build File: Includes
2
-
3
- **Feature Branch**: `001-current-state-specs`
4
-
5
- **Created**: 2026-05-30
6
-
7
- **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
-
9
- **Input**: Existing behavior documented in `docs/build-file/includes.md`
10
-
11
- ## User Scenarios & Testing *(mandatory)*
12
-
13
- ### User Story 1 - Reuse a shared task definition across multiple projects (Priority: P1)
14
-
15
- In a monorepo with multiple packages that each have a `package.json`, a developer extracts the common `npm install` task into a shared `build.npm.yaml` file at the repo root. Each package's build file declares an `includes:` entry pointing to that shared file under the alias `npm`. The `npm:install` task is then available as a dependency in each package and executes with the working directory set to the package that included it — not the location of the shared file.
16
-
17
- **Why this priority**: Includes are the canonical way to eliminate duplicated task definitions in monorepos; getting the working directory right is the core value proposition.
18
-
19
- **Independent Test**: Create a shared build file with a task that writes a file. Include it in two different package build files. Run `hammerkit npm:install` from each package context and confirm the output file is written to the respective package directory, not the shared file's directory.
20
-
21
- **Acceptance Scenarios**:
22
-
23
- 1. **Given** `build.npm.yaml` at the repo root defines task `install`, and `packages/a/.hammerkit.yaml` declares `includes: { npm: ../../build.npm.yaml }`, **When** the user runs `npm:install` in the context of package `a`, **Then** the task executes with the working directory set to `packages/a/`.
24
- 2. **Given** the same shared file is included in `packages/b/.hammerkit.yaml`, **When** `npm:install` runs in package `b`'s context, **Then** the working directory is `packages/b/`, independent of package `a`.
25
- 3. **Given** `packages/b/.hammerkit.yaml` declares `deps: [npm:install, a:build]`, **When** the build task runs, **Then** `npm:install` runs in `packages/b/`'s working directory and `a:build` runs as a reference with `packages/a/`'s working directory.
26
-
27
- ### Edge Cases
28
-
29
- - Includes differ from references in that the working directory is the location of the including build file, not the location of the included file.
30
- - Top-level `envs:` from the including build file are not visible to tasks in the included build file.
31
- - An included file can itself declare further references, mixing the two mechanisms as shown in the monorepo example.
32
-
33
- ## Requirements *(mandatory)*
34
-
35
- ### Functional Requirements
36
-
37
- - **FR-001**: The build file MUST support a top-level `includes:` map where each key is an alias and each value is a path to a shared build YAML file.
38
- - **FR-002**: Tasks from an included build file MUST be addressable using the `<alias>:<task-name>` notation from the CLI and in task `deps:` lists.
39
- - **FR-003**: When a task from an included build file executes, the working directory MUST be the directory of the build file that declared the `includes:` entry, NOT the directory of the included file.
40
- - **FR-004**: The same shared build file MAY be included by multiple build files, with each inclusion using the working directory of the including file independently.
41
- - **FR-005**: Includes and references MAY be combined in the same build file, allowing tasks from included files to coexist with tasks from referenced sub-projects as dependencies.
42
-
43
- ### Key Entities
44
-
45
- - **Include**: A named entry in the `includes:` map; the key is the alias used to prefix task names, the value is the relative path to the shared build YAML file.
46
-
47
- ## Success Criteria *(mandatory)*
48
-
49
- ### Measurable Outcomes
50
-
51
- - **SC-001**: A task sourced from an included build file runs with the working directory of the including project, not the directory where the shared file resides.
52
- - **SC-002**: The same shared task file included by two different packages produces independent executions, each in their own package directory.
53
- - **SC-003**: A build file that combines `includes:` and `references:` correctly resolves both sets of task namespaces and executes each with the correct working directory.
54
-
55
- ## Assumptions
56
-
57
- - Include paths are relative to the directory of the build file containing the `includes:` block.
58
- - Included files are standard hammerkit build YAML files and may contain any valid build file content (`tasks:`, `envs:`, etc.).
59
- - The working-directory rule applies to all task types within an included file (container tasks and local tasks alike), though the docs only illustrate local tasks.
60
- - Unlike references, includes do not require the target path to be a directory — they point directly to a `.yaml` file.
@@ -1,56 +0,0 @@
1
- # Feature Specification: Build File: References
2
-
3
- **Feature Branch**: `001-current-state-specs`
4
-
5
- **Created**: 2026-05-30
6
-
7
- **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
-
9
- **Input**: Existing behavior documented in `docs/build-file/references.md`
10
-
11
- ## User Scenarios & Testing *(mandatory)*
12
-
13
- ### User Story 1 - Run a task from a referenced sub-project (Priority: P1)
14
-
15
- A developer has a monorepo with multiple sub-directories, each containing its own build file. They declare a `references:` map in the root build file, giving each sub-project a short alias. They can then invoke tasks in those sub-projects from the CLI using the `<alias>:<task>` syntax, or declare them as dependencies in root tasks.
16
-
17
- **Why this priority**: References are the primary mechanism for composing multi-project builds from a single entry-point build file.
18
-
19
- **Independent Test**: Create a root build file with a reference to a sub-directory build file. Run `hammerkit <alias>:<task>` and confirm the task in the sub-project executes.
20
-
21
- **Acceptance Scenarios**:
22
-
23
- 1. **Given** a root build file declares `references: { foo: project/foo }`, and `project/foo/.hammerkit.yaml` contains a task named `example`, **When** the user runs `hammerkit foo:example`, **Then** the `example` task from `project/foo` executes.
24
- 2. **Given** a root task declares `foo:example` in its `deps:`, **When** that root task runs, **Then** `foo:example` is executed as a prerequisite with the working directory fixed to `project/foo`.
25
-
26
- ### Edge Cases
27
-
28
- - The working directory for tasks in a referenced build file is fixed to the directory where the referenced build file resides, not the directory of the referencing build file.
29
- - Top-level `envs:` from the referencing build file are not visible to tasks in the referenced build file.
30
- - Circular references (A references B which references A) are not discussed in the docs; behavior is assumed to be an error or undefined.
31
-
32
- ## Requirements *(mandatory)*
33
-
34
- ### Functional Requirements
35
-
36
- - **FR-001**: The build file MUST support a top-level `references:` map where each key is an alias and each value is a path to another project directory containing a build file.
37
- - **FR-002**: Tasks from a referenced build file MUST be addressable using the `<alias>:<task-name>` notation from the CLI.
38
- - **FR-003**: Tasks from a referenced build file MUST be usable as dependency entries in the referencing build file's task `deps:` list using the `<alias>:<task-name>` notation.
39
- - **FR-004**: The working directory for tasks executing in a referenced build file MUST be the directory of that referenced build file, not the directory of the referencing file.
40
-
41
- ### Key Entities
42
-
43
- - **Reference**: A named entry in the `references:` map; the key is the alias used to prefix task names, the value is the relative path to the sub-project directory.
44
-
45
- ## Success Criteria *(mandatory)*
46
-
47
- ### Measurable Outcomes
48
-
49
- - **SC-001**: A task in a referenced build file is successfully invoked via `hammerkit <alias>:<task>` and executes with the working directory set to the referenced project directory.
50
- - **SC-002**: A task in the root build file that lists `<alias>:<task>` as a dependency causes that referenced task to run before the root task's commands execute.
51
-
52
- ## Assumptions
53
-
54
- - The path in the `references:` value points to a directory, and hammerkit locates the build file (`build.yaml` or `.hammerkit.yaml`) within that directory automatically.
55
- - Reference paths are relative to the directory of the build file containing the `references:` block.
56
- - Deeply nested references (a reference to a build file that itself references another) are assumed to be supported, though not explicitly documented.
@@ -1,62 +0,0 @@
1
- # Feature Specification: Build File: Overview
2
-
3
- **Feature Branch**: `001-current-state-specs`
4
-
5
- **Created**: 2026-05-30
6
-
7
- **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
-
9
- **Input**: Existing behavior documented in `docs/build-file/README.md`
10
-
11
- ## User Scenarios & Testing *(mandatory)*
12
-
13
- ### User Story 1 - Define a project build file (Priority: P1)
14
-
15
- A developer creates a `build.yaml` (or `.hammerkit.yaml`) file at the root of their project directory. They declare top-level environment variables, one or more named tasks, references to sub-projects, and includes for shared task files. They then invoke tasks by name via the hammerkit CLI.
16
-
17
- **Why this priority**: The build file is the central configuration artifact; all other hammerkit features depend on it.
18
-
19
- **Independent Test**: Create a minimal build file with a single task and verify `hammerkit <task>` executes it.
20
-
21
- **Acceptance Scenarios**:
22
-
23
- 1. **Given** a project directory, **When** a `build.yaml` file is placed there with a valid `tasks:` block, **Then** hammerkit discovers and executes named tasks from that file via the CLI.
24
- 2. **Given** a build file with a top-level `envs:` block, **When** a task is run, **Then** the declared environment variables are available to the task commands.
25
- 3. **Given** a build file with `references:` and `includes:` blocks, **When** hammerkit starts, **Then** it loads the referenced and included build files and makes their tasks available under the appropriate namespaced names.
26
-
27
- ### Edge Cases
28
-
29
- - A build file with no `tasks:` block is valid but produces no executable targets.
30
- - The filename may be `build.yaml` or `.hammerkit.yaml`; behavior is identical.
31
- - Top-level `envs:` are scoped to the build file where they are declared and do not propagate to referenced or included files.
32
-
33
- ## Requirements *(mandatory)*
34
-
35
- ### Functional Requirements
36
-
37
- - **FR-001**: The system MUST read build configuration from a YAML file named `build.yaml` or `.hammerkit.yaml` located in the project directory.
38
- - **FR-002**: The build file MUST support a top-level `envs:` map for declaring environment variables shared across all tasks in that file.
39
- - **FR-003**: The build file MUST support a top-level `tasks:` map where each key is a task name and the value is the task configuration.
40
- - **FR-004**: The build file MUST support a top-level `references:` map pointing to other build files in sub-directories.
41
- - **FR-005**: The build file MUST support a top-level `includes:` map pointing to shared build files whose tasks are merged under a namespace.
42
- - **FR-006**: Tasks declared in a build file MUST be invocable by name via the hammerkit CLI.
43
-
44
- ### Key Entities
45
-
46
- - **Build File**: A YAML file (`build.yaml` / `.hammerkit.yaml`) containing `envs`, `tasks`, `references`, and `includes` top-level keys.
47
- - **Task**: A named unit of work inside the `tasks:` map; may declare commands, dependencies, source files, and generated outputs.
48
- - **Reference**: A named pointer to another project directory containing its own build file.
49
- - **Include**: A named pointer to a shared build YAML file whose tasks are reused with the working directory of the including file.
50
-
51
- ## Success Criteria *(mandatory)*
52
-
53
- ### Measurable Outcomes
54
-
55
- - **SC-001**: A build file with all four top-level sections (`envs`, `tasks`, `references`, `includes`) loads without errors and all tasks are accessible via the CLI.
56
- - **SC-002**: Environment variables defined in the top-level `envs:` block are resolved correctly within tasks in the same file and are not visible in referenced or included files.
57
-
58
- ## Assumptions
59
-
60
- - The primary filename used in examples is `.hammerkit.yaml`; `build.yaml` is also accepted based on the docs description.
61
- - The `caches:` top-level key (introduced in 1.6.0) exists in the build file schema but is covered by its own spec page.
62
- - Task-level details (image, cmds, deps, src, generates) are covered by the Task spec pages, not this overview.
@@ -1,85 +0,0 @@
1
- # Feature Specification: CLI: clean
2
-
3
- **Feature Branch**: `001-current-state-specs`
4
-
5
- **Created**: 2026-05-30
6
-
7
- **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
-
9
- **Input**: Existing behavior documented in `docs/cli/clean.md`
10
-
11
- ## User Scenarios & Testing *(mandatory)*
12
-
13
- ### User Story 1 - Remove generated outputs and local cache state for a clean rebuild (Priority: P1)
14
-
15
- A developer suspects their build is producing stale results due to a previous cached state. They run `hammerkit clean` to delete all generated directories and files as well as hammerkit's per-task cache state for the current checkout, then re-run tasks to verify everything builds from scratch.
16
-
17
- **Why this priority**: The primary purpose of `clean` is to reset local state so that builds are fully reproducible without prior artifacts.
18
-
19
- **Independent Test**: Run a task that produces a `generates` output. Run `hammerkit clean`. Confirm the generated output no longer exists and that hammerkit re-executes the task on the next run regardless of unchanged inputs.
20
-
21
- **Acceptance Scenarios**:
22
-
23
- 1. **Given** tasks have been run and generated outputs and cache state exist, **When** `hammerkit clean` is run, **Then** all generated outputs and per-task local cache state are removed.
24
-
25
- 2. **Given** `hammerkit clean` has completed, **When** a task is run again with unchanged inputs, **Then** hammerkit re-executes the task because the local cache state is absent.
26
-
27
- ### User Story 2 - Drop stored remote/backend cache results to force a full rebuild (Priority: P2)
28
-
29
- A developer encounters a persistent caching issue and needs to force the next run to rebuild from scratch even against a configured cache backend (local result store or remote, e.g. S3). They run `hammerkit clean --cache` to also purge stored results from the backend.
30
-
31
- **Why this priority**: The `--cache` flag addresses a deeper reset need when the standard clean is insufficient due to a populated cache backend.
32
-
33
- **Independent Test**: Configure a cache backend. Run tasks so results are stored in the backend. Run `hammerkit clean --cache`. Confirm the backend has no stored results for the project and that the next run rebuilds all tasks.
34
-
35
- **Acceptance Scenarios**:
36
-
37
- 1. **Given** a configured cache backend with stored task results, **When** `hammerkit clean --cache` is run, **Then** the stored results in the backend (local `~/.hammerkit/remote-cache` or remote) are deleted in addition to generated outputs and local cache state.
38
-
39
- 2. **Given** `--cache` is omitted, **When** `hammerkit clean` is run, **Then** stored results in the cache backend are preserved; only local generated outputs and per-task cache state are removed.
40
-
41
- ### User Story 3 - Clean a subset of tasks and services using label filters (Priority: P2)
42
-
43
- A developer wants to clean only a specific subset of tasks without affecting others. They use `--filter` or `--exclude` with label selectors to target or omit specific tasks and services.
44
-
45
- **Why this priority**: Label-scoped clean reduces disruption to unrelated tasks in large build files.
46
-
47
- **Independent Test**: Tag two tasks with different labels. Run both. Run `hammerkit clean --filter <label>`. Confirm only the filtered task's outputs and cache state are removed while the other task's outputs remain.
48
-
49
- **Acceptance Scenarios**:
50
-
51
- 1. **Given** tasks are labeled, **When** `hammerkit clean --filter <label>` is run, **Then** only the tasks and services matching the label are cleaned.
52
-
53
- 2. **Given** tasks are labeled, **When** `hammerkit clean --exclude <label>` is run, **Then** tasks and services matching the label are skipped and all others are cleaned.
54
-
55
- ### Edge Cases
56
-
57
- - Running `hammerkit clean` when no tasks have been run (no generated outputs, no cache state) should complete without error.
58
- - `hammerkit clean --cache` removes the local result store at `~/.hammerkit/remote-cache` or a configured remote backend, not just the per-checkout cache state.
59
- - Label filters apply to both tasks and services, consistent with their behavior in other commands.
60
-
61
- ## Requirements *(mandatory)*
62
-
63
- ### Functional Requirements
64
-
65
- - **FR-001**: System MUST remove all generated outputs (files and directories declared via `generates`) for all tasks and services in the current checkout when `hammerkit clean` is run.
66
- - **FR-002**: System MUST remove the per-task local cache state for the current checkout when `hammerkit clean` is run.
67
- - **FR-003**: When `--cache` is supplied, system MUST additionally delete stored results from the configured cache backend (defaulting to `~/.hammerkit/remote-cache` for local backends).
68
- - **FR-004**: System MUST support `-f`/`--filter` and `-e`/`--exclude` label options to restrict which tasks and services are cleaned.
69
- - **FR-005**: System MUST complete without error when there are no generated outputs or cache state to remove.
70
-
71
- ## Success Criteria *(mandatory)*
72
-
73
- ### Measurable Outcomes
74
-
75
- - **SC-001**: After `hammerkit clean`, no generated output directories or files from any task's `generates` field exist in the working tree.
76
- - **SC-002**: After `hammerkit clean`, running any task re-executes it fully regardless of whether its inputs have changed.
77
- - **SC-003**: After `hammerkit clean --cache`, the cache backend contains no stored results for the project and the next run rebuilds all tasks from source.
78
- - **SC-004**: After `hammerkit clean --filter <label>`, only tasks matching the label have their outputs and cache state removed; all other task outputs remain intact.
79
-
80
- ## Assumptions
81
-
82
- - "Per-task cache state" refers to the hammerkit-internal tracking of input hashes used to determine whether a task is up to date; this is distinct from stored build results in the cache backend.
83
- - The `--cache` flag targets whatever backend is configured in the build file's `caches` section, defaulting to the local result store at `~/.hammerkit/remote-cache`.
84
- - The `clean` command does not prompt for confirmation; it is destructive by design.
85
- - There is no `--dry-run` option documented; the command always performs the deletion.
@@ -1,85 +0,0 @@
1
- # Feature Specification: CLI: down
2
-
3
- **Feature Branch**: `001-current-state-specs`
4
-
5
- **Created**: 2026-05-30
6
-
7
- **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
-
9
- **Input**: Existing behavior documented in `docs/cli/down.md`
10
-
11
- ## User Scenarios & Testing *(mandatory)*
12
-
13
- ### User Story 1 - Stopping all running services (Priority: P1)
14
-
15
- A developer has finished their work session and wants to free the resources held by services that were started with `hammerkit up` (foreground or daemon). They run `hammerkit down` and all services defined in the build file are stopped.
16
-
17
- **Why this priority**: `down` is the natural complement to `up` and the primary way to cleanly release containerized service resources.
18
-
19
- **Independent Test**: Start services with `hammerkit up --daemon`, then run `hammerkit down` and verify that all service containers are no longer running.
20
-
21
- **Acceptance Scenarios**:
22
-
23
- 1. **Given** services started with `hammerkit up --daemon`, **When** the user runs `hammerkit down`, **Then** all services defined in the build file are stopped and exit cleanly.
24
-
25
- 2. **Given** services started with `hammerkit up` (foreground, now interrupted), **When** the user runs `hammerkit down`, **Then** any remaining service containers are stopped.
26
-
27
- 3. **Given** no services currently running, **When** the user runs `hammerkit down`, **Then** the command exits with code 0 without error.
28
-
29
- ### User Story 2 - Stopping a subset of services by label (Priority: P2)
30
-
31
- A developer is running multiple services and wants to stop only a specific subset without affecting others. They use `--filter` or `--exclude` to target the relevant services.
32
-
33
- **Why this priority**: Label filtering is consistent across all service-related commands and supports fine-grained lifecycle control in complex builds.
34
-
35
- **Independent Test**: Start two labeled services, run `hammerkit down -f <label>` matching only one, and verify the other remains running.
36
-
37
- **Acceptance Scenarios**:
38
-
39
- 1. **Given** multiple services with different labels, **When** the user runs `hammerkit down -f <label>`, **Then** only services matching the label are stopped; others remain running.
40
-
41
- 2. **Given** multiple services with different labels, **When** the user runs `hammerkit down -e <label>`, **Then** services matching the excluded label are not stopped; all others are stopped.
42
-
43
- ### User Story 3 - Stopping services in a named environment (Priority: P3)
44
-
45
- A developer has services running in a Kubernetes environment and wants to stop them without affecting local Docker services. They pass `--env <name>` to target that environment.
46
-
47
- **Why this priority**: Multi-environment support is consistent with `up` but is a power-user scenario.
48
-
49
- **Independent Test**: Given services running in a named Kubernetes environment, run `hammerkit down --env <name>` and verify those services stop while local Docker services are unaffected.
50
-
51
- **Acceptance Scenarios**:
52
-
53
- 1. **Given** services running in a named environment, **When** the user runs `hammerkit down --env <name>`, **Then** services in that environment are stopped.
54
-
55
- ### Edge Cases
56
-
57
- - `hammerkit down` with no running services must not error.
58
- - `hammerkit down -f <label>` where no services match the label must exit with code 0 without affecting other services.
59
- - `--filter` and `--exclude` used together must apply both constraints; services matching both conditions are excluded.
60
-
61
- ## Requirements *(mandatory)*
62
-
63
- ### Functional Requirements
64
-
65
- - **FR-001**: System MUST stop all services defined in the build file when `hammerkit down` is run without filter options.
66
- - **FR-002**: System MUST support label-based inclusion filtering via `-f / --filter <key=value>` to stop only matching services.
67
- - **FR-003**: System MUST support label-based exclusion filtering via `-e / --exclude <key=value>` to skip stopping matching services.
68
- - **FR-004**: System MUST support an `--env <name>` option to stop services in a configured named environment.
69
- - **FR-005**: System MUST exit with code 0 when no services are running (idempotent stop).
70
-
71
- ## Success Criteria *(mandatory)*
72
-
73
- ### Measurable Outcomes
74
-
75
- - **SC-001**: After `hammerkit down`, no service containers defined in the build file remain in a running state.
76
- - **SC-002**: `hammerkit down` exits with code 0 regardless of whether services were running before the command.
77
- - **SC-003**: With `-f <label>`, only services bearing that label are stopped; services with other labels continue running.
78
- - **SC-004**: With `--env <name>`, only services in the named environment are stopped; services in other environments are unaffected.
79
-
80
- ## Assumptions
81
-
82
- - `hammerkit down` targets the same set of services as the corresponding `hammerkit up` invocation when no filters are applied.
83
- - The command does not require that services were started specifically via `hammerkit up`; it stops matching service containers regardless of how they were started.
84
- - `down` has no `--daemon`, `--concurrency`, `--watch`, `--log`, or `--cache` options (these do not appear in the documented option list).
85
- - Stopping a service that is not running is treated as a success (idempotent), consistent with the "no error on empty" edge case documented implicitly.
@@ -1,101 +0,0 @@
1
- # Feature Specification: CLI: execute
2
-
3
- **Feature Branch**: `001-current-state-specs`
4
-
5
- **Created**: 2026-05-30
6
-
7
- **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
-
9
- **Input**: Existing behavior documented in `docs/cli/execute.md`
10
-
11
- ## User Scenarios & Testing *(mandatory)*
12
-
13
- ### User Story 1 - Running a task by name (Priority: P1)
14
-
15
- A developer wants to build a specific artifact. They run `hammerkit <task-name>` (or `hammerkit run <task-name>`) and hammerkit executes that task and all its declared dependencies inside the appropriate containers.
16
-
17
- **Why this priority**: Executing tasks is the core purpose of the tool; all other features support this.
18
-
19
- **Independent Test**: Given a build file with a task named `example`, run `hammerkit example` and verify it completes successfully and produces the expected output.
20
-
21
- **Acceptance Scenarios**:
22
-
23
- 1. **Given** a build file with a task `build`, **When** the user runs `hammerkit build`, **Then** hammerkit executes the `build` task (and its dependencies) and exits with code 0 on success.
24
-
25
- 2. **Given** a build file with a task `build`, **When** the user runs `hammerkit run build`, **Then** the result is identical to running `hammerkit build`.
26
-
27
- 3. **Given** a task name that does not exist in the build file, **When** the user runs `hammerkit <nonexistent>`, **Then** hammerkit exits with a non-zero code and a meaningful error message.
28
-
29
- ### User Story 2 - Filtering tasks by labels (Priority: P2)
30
-
31
- A monorepo developer wants to run only the iOS build tasks. They use `--filter` to include only tasks with a matching label or `--exclude` to skip tasks with a given label, so unrelated tasks are not executed.
32
-
33
- **Why this priority**: Label-based filtering is a key scalability feature for large builds with many tasks.
34
-
35
- **Independent Test**: Given a build file with tasks labeled `build=ios` and `build=android`, run `hammerkit -f build=ios` and verify only the iOS tasks (and their dependencies) execute.
36
-
37
- **Acceptance Scenarios**:
38
-
39
- 1. **Given** tasks with label `type=build` and tasks without that label, **When** the user runs `hammerkit -f type=build`, **Then** only tasks matching `type=build` are executed; dependent tasks without that label may still run to satisfy dependencies.
40
-
41
- 2. **Given** tasks with label `build=ios`, **When** the user runs `hammerkit -e build=ios`, **Then** tasks with `build=ios` are excluded; if a dependent task also matches the excluded label, it is excluded too.
42
-
43
- 3. **Given** multiple label filters, **When** the user provides multiple `-f` arguments, **Then** only tasks matching all specified labels are included.
44
-
45
- ### User Story 3 - Controlling concurrency, caching, and log mode (Priority: P3)
46
-
47
- A developer running on a slow machine wants to limit parallel workers, or a CI engineer wants checksum-based caching and grouped log output. They pass the relevant flags to tune hammerkit's behavior for their environment.
48
-
49
- **Why this priority**: These are power-user options that improve CI reliability and local performance but are not needed for basic usage.
50
-
51
- **Independent Test**: Run `hammerkit run <task> --concurrency 1` and verify tasks execute sequentially (no interleaved log lines from parallel workers).
52
-
53
- **Acceptance Scenarios**:
54
-
55
- 1. **Given** a build file with multiple independent tasks, **When** the user runs `hammerkit -c 1`, **Then** tasks execute one at a time rather than in parallel.
56
-
57
- 2. **Given** a CI environment, **When** hammerkit detects CI, **Then** `--log` defaults to `live` and `--cache` defaults to `checksum` without requiring explicit flags.
58
-
59
- 3. **Given** a local (non-CI) environment, **When** hammerkit runs without explicit `--log` or `--cache` flags, **Then** `--log` defaults to `interactive` and `--cache` defaults to `modify-date`.
60
-
61
- 4. **Given** a task with `src` files, **When** `--cache none` is specified, **Then** the task runs unconditionally without checking whether inputs have changed.
62
-
63
- 5. **Given** a task with `src` files that have not changed, **When** `--watch` is specified, **Then** hammerkit watches those files and re-executes the task when they change.
64
-
65
- ### Edge Cases
66
-
67
- - Specifying both `--filter` and `--exclude` with overlapping labels must resolve deterministically (exclude takes precedence for matched tasks).
68
- - `--concurrency 0` or negative values should be rejected with a clear error.
69
- - `--env <name>` is accepted but its behavior is defined by the environment configuration, not by the execute command itself.
70
-
71
- ## Requirements *(mandatory)*
72
-
73
- ### Functional Requirements
74
-
75
- - **FR-001**: System MUST execute a task by name when the user supplies the task name as the first argument (with or without the explicit `run` subcommand).
76
- - **FR-002**: System MUST execute all declared dependency tasks before the named task.
77
- - **FR-003**: System MUST support label-based inclusion filtering via `-f / --filter <key=value>` where only tasks matching the label are directly executed (dependencies may still run).
78
- - **FR-004**: System MUST support label-based exclusion filtering via `-e / --exclude <key=value>` where tasks (and their dependents) matching the label are skipped.
79
- - **FR-005**: System MUST support a `-c / --concurrency <number>` option controlling the number of parallel worker threads (default: 4).
80
- - **FR-006**: System MUST support a `-w / --watch` flag that re-executes tasks when their source files change.
81
- - **FR-007**: System MUST support a `--cache <method>` option with choices `checksum`, `modify-date`, and `none`.
82
- - **FR-008**: System MUST support a `-l / --log <mode>` option with choices `interactive`, `live`, and `grouped`.
83
- - **FR-009**: System MUST default `--log` to `interactive` and `--cache` to `modify-date` outside CI, and to `live` and `checksum` inside CI.
84
- - **FR-010**: System MUST support an `--env <name>` option to target a configured named environment.
85
-
86
- ## Success Criteria *(mandatory)*
87
-
88
- ### Measurable Outcomes
89
-
90
- - **SC-001**: `hammerkit <task>` and `hammerkit run <task>` produce identical exit codes and output for the same task.
91
- - **SC-002**: With `-f type=build`, only tasks bearing the `type=build` label are directly invoked; the set of executed tasks is smaller than running without the filter.
92
- - **SC-003**: With `-e build=ios`, no task bearing `build=ios` appears in the execution log.
93
- - **SC-004**: With `-c 1`, task execution log lines are never interleaved across concurrent workers.
94
- - **SC-005**: With `--cache none`, a task runs even when no source files have changed since the last run.
95
-
96
- ## Assumptions
97
-
98
- - `hammerkit` with no subcommand and no task name runs all tasks (or the default task if one is defined); the docs show a task name is required for named execution.
99
- - `--filter` and `--exclude` accept `key=value` pairs; partial-key matching (e.g., filtering by key name only) is not documented and assumed unsupported.
100
- - The `--watch` flag watches the `src` files declared on the task; the exact file-watching mechanism is an implementation detail.
101
- - CI is detected automatically via standard CI environment variables (e.g., `CI=true`); this is an informed guess not explicitly stated in the docs.
@@ -1,64 +0,0 @@
1
- # Feature Specification: CLI: init
2
-
3
- **Feature Branch**: `001-current-state-specs`
4
-
5
- **Created**: 2026-05-30
6
-
7
- **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
-
9
- **Input**: Existing behavior documented in `docs/cli/init.md`
10
-
11
- ## User Scenarios & Testing *(mandatory)*
12
-
13
- ### User Story 1 - Bootstrapping a new project (Priority: P1)
14
-
15
- A developer starts a brand-new project and wants to adopt hammerkit. They run `hammerkit init` in the project root, which creates a ready-to-edit `.hammerkit.yaml` with a working example task and appends the hammerkit cache directory to `.gitignore` so build artifacts are not accidentally committed.
16
-
17
- **Why this priority**: `init` is the mandatory first step for every new hammerkit user; without it the rest of the tool cannot be used.
18
-
19
- **Independent Test**: In an empty directory, run `hammerkit init` and verify that `.hammerkit.yaml` and `.gitignore` are created (or updated) with the documented content.
20
-
21
- **Acceptance Scenarios**:
22
-
23
- 1. **Given** an empty directory with no existing build file, **When** the user runs `hammerkit init`, **Then** a `.hammerkit.yaml` file is created containing an `envs: {}` block, a `tasks:` block with an `example` task using the `alpine` image and the command `echo "it's Hammer Time!"`.
24
-
25
- 2. **Given** an empty directory with no existing `.gitignore`, **When** the user runs `hammerkit init`, **Then** a `.gitignore` file is created containing the entry `.hammerkit`.
26
-
27
- 3. **Given** a directory that already has a `.gitignore`, **When** the user runs `hammerkit init`, **Then** the `.hammerkit` entry is appended to the existing `.gitignore` without overwriting other entries.
28
-
29
- 4. **Given** a directory that already has a `.hammerkit.yaml`, **When** the user runs `hammerkit init`, **Then** the command succeeds (or warns) without overwriting the existing build file.
30
-
31
- ### Edge Cases
32
-
33
- - If the user lacks write permissions in the working directory, `init` must exit with a non-zero code and a meaningful error message.
34
- - Running `init` when a `.hammerkit.yaml` already exists must not destroy existing configuration.
35
-
36
- ## Requirements *(mandatory)*
37
-
38
- ### Functional Requirements
39
-
40
- - **FR-001**: System MUST create a `.hammerkit.yaml` file in the current working directory when `hammerkit init` is run and no build file exists.
41
- - **FR-002**: The generated `.hammerkit.yaml` MUST contain an `envs: {}` key and a `tasks:` key with at least one example task using the `alpine` image.
42
- - **FR-003**: System MUST create or update a `.gitignore` file in the current working directory to include the `.hammerkit` cache directory entry.
43
- - **FR-004**: System MUST print confirmation output indicating each file that was created or updated, including the file path.
44
-
45
- ### Key Entities
46
-
47
- - **Build file (`.hammerkit.yaml`)**: The hammerkit project configuration file. Created by `init` with a minimal, runnable example task.
48
- - **Cache directory entry (`.hammerkit`)**: The directory hammerkit uses for local caching; excluded from version control via `.gitignore`.
49
-
50
- ## Success Criteria *(mandatory)*
51
-
52
- ### Measurable Outcomes
53
-
54
- - **SC-001**: After `hammerkit init`, the file `.hammerkit.yaml` exists in the working directory and is parseable as valid YAML with the documented structure.
55
- - **SC-002**: After `hammerkit init`, the file `.gitignore` in the working directory contains the line `.hammerkit`.
56
- - **SC-003**: `hammerkit init` exits with code 0 and prints at least two confirmation lines (one per created/updated file).
57
- - **SC-004**: Running `hammerkit run example` immediately after `hammerkit init` in a Docker-enabled environment succeeds without manual editing.
58
-
59
- ## Assumptions
60
-
61
- - `init` creates files relative to the current working directory (not a project-root detection heuristic).
62
- - The console confirmation output format is a checkmark prefix followed by the absolute path of each file, as shown in the docs example.
63
- - If `.hammerkit.yaml` already exists, `init` skips creation rather than overwriting; this is an informed guess based on the "non-destructive bootstrap" intent.
64
- - The `--file` global option does not affect where `init` writes the build file; it always writes `.hammerkit.yaml`.