this-is-enough 0.1.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 (4) hide show
  1. package/AGENTS.md +546 -0
  2. package/README.md +126 -0
  3. package/bin/cli.js +461 -0
  4. package/package.json +16 -0
package/AGENTS.md ADDED
@@ -0,0 +1,546 @@
1
+ # AGENTS.md - Project Agent Operating Rules
2
+
3
+ ## Purpose
4
+ Operate this repository with a strict single-active-requirement workflow.
5
+ Enforce one active lane at a time while still allowing controlled pause/switch/resume.
6
+ Keep architecture, decisions, and validation evidence consistent.
7
+
8
+ ---
9
+
10
+ ## Single Source of Truth (SSOT)
11
+
12
+ ### 1) Active Requirement Lock (SSOT)
13
+ - `reqs/ACTIVE.md` is the only lock + pointer for the active requirement.
14
+ - If `reqs/ACTIVE.md` exists:
15
+ - One requirement is active (`DRAFT` or `ACTIVE`).
16
+ - Creating a new `reqs/REQ-*` directory is forbidden.
17
+ - If `reqs/ACTIVE.md` does not exist:
18
+ - No requirement is active.
19
+ - Creating a new requirement is allowed.
20
+
21
+ Note:
22
+ - `reqs/ACTIVE.md` exists only while a requirement is active.
23
+ - Inactive states (`PAUSED`, `BLOCKED`, `DONE`) are recorded in each requirement's `STATUS.md`.
24
+
25
+ ### 2) Current Architecture (SSOT for current design)
26
+ - `ARCHITECTURE.md` represents the current system design and core logic.
27
+ - Update `ARCHITECTURE.md` only when a validated phase introduces architecture-impacting changes.
28
+
29
+ ### 3) Decisions (SSOT for why)
30
+ - ADRs live in `docs/adr/`.
31
+ - Add or update an ADR only when a validated phase introduces architecture-impacting changes.
32
+
33
+ ### 4) Backlog Intake (SSOT for independent incoming requests)
34
+ - `reqs/INBOX.md` is the global intake/backlog for independent requests.
35
+ - While `reqs/ACTIVE.md` exists:
36
+ - independent new requests must be appended to `reqs/INBOX.md`
37
+ - they must not be converted into a new `REQ-*` until unlocked (or after explicit pause/switch)
38
+
39
+ ### 5) Interrupt Exception State (SSOT for urgent exception lane)
40
+ - `reqs/INTERRUPT.md` is the only active record for an urgent interrupt.
41
+ - At most one urgent interrupt may be active at a time.
42
+ - After completion, move `reqs/INTERRUPT.md` to `reqs/interrupts/INT-*.md` and remove the active interrupt file.
43
+
44
+ ---
45
+
46
+ ## Repository Layout (Required)
47
+
48
+ ```text
49
+ ARCHITECTURE.md
50
+ AGENTS.md
51
+ docs/
52
+ adr/
53
+ reqs/
54
+ INBOX.md # global intake for independent requests
55
+ ACTIVE.md # present only while a requirement is active
56
+ INTERRUPT.md # present only while an urgent interrupt is active
57
+ interrupts/ # archive of completed interrupts
58
+ INT-YYYYMMDD-HHMM-<slug>.md
59
+ REQ-0001-<slug>/
60
+ REQUEST.md # requirement draft/spec
61
+ ROADMAP.md # full roadmap (required unless Mini Roadmap criteria are fully met)
62
+ STATUS.md # status + phase state + inactive-state record
63
+ DEFERRED.md # req-local parking lot for derived non-mandatory ideas
64
+ phases/
65
+ PHASE-01/
66
+ PLAN.md
67
+ VALIDATION_LOG.md
68
+ PHASE-02/
69
+ PLAN.md
70
+ VALIDATION_LOG.md
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Day-0 Bootstrap (MUST)
76
+
77
+ Run once when adopting this workflow in a new repository.
78
+
79
+ 1) Create required directories
80
+ - `docs/adr/`
81
+ - `reqs/`
82
+ - `reqs/interrupts/`
83
+
84
+ 2) Create required baseline files
85
+ - `ARCHITECTURE.md` (initial architecture baseline; can start minimal)
86
+ - `reqs/INBOX.md` (initialize with header and item schema)
87
+
88
+ 3) Initial lock/interrupt state must be clean
89
+ - `reqs/ACTIVE.md` must not exist at bootstrap completion
90
+ - `reqs/INTERRUPT.md` must not exist at bootstrap completion
91
+
92
+ 4) First requirement starts only after bootstrap
93
+ - create `REQ-*` through lifecycle section `A) Create New Requirement`
94
+ - confirm via section `B) Confirm Requirement` (`full` or `mini` mode)
95
+
96
+ ---
97
+
98
+ ## Status Model (Requirement-Level)
99
+
100
+ Allowed statuses:
101
+ - `DRAFT`: requirement exists and is being drafted (lock is active)
102
+ - `ACTIVE`: confirmed and currently being executed (lock is active)
103
+ - `PAUSED`: intentionally stopped to work on another requirement (lock not held)
104
+ - `BLOCKED`: cannot progress due to external dependency (lock not held)
105
+ - `DONE`: fully completed (lock not held)
106
+
107
+ Rules:
108
+ - Only one requirement may be in `DRAFT` or `ACTIVE` at a time.
109
+ - `PAUSED`/`BLOCKED` requirements must include resume metadata in `STATUS.md`.
110
+
111
+ Definitions:
112
+ - `activate requirement`: set target requirement `STATUS.md` to `ACTIVE` and set `reqs/ACTIVE.md` to that target.
113
+ - `switch execution`: perform switch steps atomically (one continuous operation) without per-step user confirmation; report the final switch result. Ask for user input only if target selection or priority is ambiguous.
114
+
115
+ ---
116
+
117
+ ## Hard Constraints (MUST)
118
+
119
+ 1) Single active requirement
120
+ - Only one requirement lane may be active at a time (`DRAFT` or `ACTIVE`).
121
+ - If `reqs/ACTIVE.md` exists, no new `REQ-*` directory may be created unless pause/switch flow is completed.
122
+
123
+ 2) Single urgent interrupt exception
124
+ - While `reqs/ACTIVE.md` exists, one temporary interrupt lane may be opened through `reqs/INTERRUPT.md`.
125
+ - Interrupt eligibility uses a strict 3-question gate (all `yes` required):
126
+ - Q1 (`impact_now`): Is there immediate production/customer/business impact that cannot wait? (`yes` for outage, data risk, active security issue, release-blocking incident)
127
+ - Q2 (`timebox_fit`): Can a safe minimal fix be completed within <= 8 hours?
128
+ - Q3 (`scope_safe`): Can it be fixed without architecture-impacting changes (no external contract change, no schema/migration, no boundary/topology/security model change)?
129
+ - Priority requirement:
130
+ - only `P0` or `P1` may use interrupt flow
131
+ - During an interrupt, do not create a new `REQ-*`; execute only the interrupt scope.
132
+ - At most one `reqs/INTERRUPT.md` may exist at a time.
133
+ - If any gate answer is `no`, do not open interrupt flow; route to normal pause/switch + `REQ-*` flow.
134
+
135
+ 3) No new requirement creation while locked
136
+ - While `reqs/ACTIVE.md` exists:
137
+ - forbidden: creating a new `REQUEST.md` for another requirement
138
+ - forbidden: creating a new `REQ-*` folder
139
+ - allowed alternatives:
140
+ - append req-derived/non-mandatory ideas to active requirement `DEFERRED.md`
141
+ - append independent new requests to `reqs/INBOX.md`
142
+ - use `reqs/INTERRUPT.md` flow for qualifying urgent interrupts
143
+
144
+ 4) Agent must announce lock/interrupt state
145
+ - At session start and before new planning:
146
+ - check `reqs/INTERRUPT.md` and `reqs/ACTIVE.md`
147
+ - if `reqs/INTERRUPT.md` exists, announce interrupt `id`, `status`, `reason`, `related_active`
148
+ - else if `reqs/ACTIVE.md` exists, announce active requirement `id`, `path`, `status`, `current_phase`
149
+ - else announce no active requirement and that new creation is allowed
150
+
151
+ 5) Requirement confirmation rule
152
+ - Requirement is confirmed by one of:
153
+ - Full mode: `ROADMAP.md` exists.
154
+ - Mini mode: `REQUEST.md` contains a `Mini Roadmap` section and all criteria below are `yes/no`-exactly satisfied:
155
+ - `single_phase: yes`
156
+ - `architecture_impact: no`
157
+ - `external_contract_change: no`
158
+ - `data_model_change: no`
159
+ - `security_or_topology_change: no`
160
+ - If any Mini mode criterion becomes false during execution, create `ROADMAP.md` immediately and continue in Full mode.
161
+
162
+ 6) Architecture-impact gated updates
163
+ - After each phase validation:
164
+ - `VALIDATION_LOG.md` must record:
165
+ - `architecture_impact: yes|no`
166
+ - `architecture_impact_reason: <one-line reason>`
167
+ - if `architecture_impact: yes`:
168
+ - write/update ADR in `docs/adr/`
169
+ - update `ARCHITECTURE.md`
170
+ - if `architecture_impact: no`:
171
+ - ADR/`ARCHITECTURE.md` updates are optional for that phase
172
+
173
+ Architecture-impacting change criteria (MUST):
174
+ - classify a phase as architecture-impacting when at least one is true:
175
+ 1. external interface contract changed (API route/schema/status/event/topic/CLI contract)
176
+ 2. data model or persistence changed (schema/index/migration/storage model)
177
+ 3. system boundary/topology changed (service split/merge, sync<->async flow, queue/broker introduction)
178
+ 4. security/trust model changed (auth/authz, secret handling, tenancy/isolation, compliance control)
179
+ 5. reliability/performance design policy changed (caching strategy, consistency model, retry/idempotency policy, SLO-critical path)
180
+ - non-architecture-impacting examples:
181
+ - bug fixes or refactors without external contract/data model/boundary changes
182
+ - test-only changes, docs-only changes, copy/style/UI-only changes
183
+ - tie-breaker:
184
+ - if uncertain, classify as `architecture_impact: yes` and update ADR + `ARCHITECTURE.md`.
185
+
186
+ 7) Inactive-state record rule
187
+ - When moving to `PAUSED` or `BLOCKED`, `STATUS.md` must record:
188
+ - `changed_at`
189
+ - `reason`
190
+ - `resume_condition`
191
+ - `next_action`
192
+
193
+ 8) Safe switch rule
194
+ - Switching to a different requirement is allowed only by:
195
+ 1. recording a checkpoint in current phase `VALIDATION_LOG.md`
196
+ 2. setting current requirement `STATUS.md` to `PAUSED` or `BLOCKED`
197
+ 3. removing/updating `reqs/ACTIVE.md`
198
+ 4. activating the target requirement by rule:
199
+ - if target status is `DRAFT`, apply lifecycle section `B) Confirm Requirement`
200
+ - if target status is `PAUSED` or `BLOCKED`, apply lifecycle section `G) Resume Paused/Blocked Requirement`
201
+ 5. execute switch atomically and report result summary
202
+
203
+ 9) Backlog separation rule
204
+ - `DEFERRED.md` is only for ideas derived from the currently active requirement.
205
+ - `reqs/INBOX.md` is for independent/external requests.
206
+ - Do not mix them.
207
+
208
+ ---
209
+
210
+ ## Session Start Checklist (MUST)
211
+
212
+ 1) Check `reqs/INTERRUPT.md` and `reqs/ACTIVE.md`
213
+ - If `reqs/INTERRUPT.md` exists:
214
+ - announce active interrupt context
215
+ - proceed only within interrupt scope until closed
216
+ - Else if `reqs/ACTIVE.md` exists:
217
+ - announce active requirement context
218
+ - proceed within that requirement
219
+ - Else:
220
+ - announce unlocked state and allow creating a new requirement
221
+
222
+ 2) If interrupt is active, read in this order
223
+ 1. `reqs/INTERRUPT.md`
224
+ 2. `reqs/ACTIVE.md`
225
+ 3. `<active_req_path>/STATUS.md`
226
+ 4. current phase `PLAN.md` + `VALIDATION_LOG.md` (if exists)
227
+ 5. conditionally read `ARCHITECTURE.md` only when at least one is true:
228
+ - current interrupt scope may hit any architecture-impacting criterion
229
+ - latest validation context indicates `architecture_impact: yes`
230
+ - this session just switched/resumed active requirement
231
+ 6. conditionally read related ADRs using `ADR selective read protocol` below
232
+
233
+ 3) If active requirement only (no interrupt), read in this order
234
+ 1. `reqs/ACTIVE.md`
235
+ 2. `<active_req_path>/STATUS.md`
236
+ 3. `<active_req_path>/REQUEST.md`
237
+ 4. `<active_req_path>/ROADMAP.md` (if exists)
238
+ 5. current phase `PLAN.md` + `VALIDATION_LOG.md` (if exists)
239
+ 6. conditionally read `ARCHITECTURE.md` only when at least one is true:
240
+ - current planned change may hit any architecture-impacting criterion
241
+ - latest validation context indicates `architecture_impact: yes`
242
+ - this session just switched/resumed active requirement
243
+ 7. conditionally read related ADRs using `ADR selective read protocol` below
244
+
245
+ 4) If unlocked and `reqs/INBOX.md` exists
246
+ - review top `open` backlog items before creating a new requirement
247
+
248
+ ADR selective read protocol (MUST):
249
+ 1. scan ADR filenames/titles in `docs/adr/` first (do not read all ADR bodies by default)
250
+ 2. for candidate ADRs, read top metadata first
251
+ 3. fully read only ADRs that match the current change scope
252
+ 4. if matched ADR has `supersedes`, read the superseded ADR one hop for context
253
+
254
+ ---
255
+
256
+ ## Requirement Lifecycle (MUST)
257
+
258
+ ### Intake Rule (Always)
259
+ When a new item appears during work:
260
+ 1. if it is required to complete current requirement acceptance, include it in current requirement plan/scope
261
+ 2. if it is derived from current requirement but not required now, append to `<active_req>/DEFERRED.md`
262
+ 3. if it is an independent/external request, append to `reqs/INBOX.md`
263
+
264
+ ### A) Create New Requirement (Only when unlocked)
265
+ Precondition:
266
+ - `reqs/ACTIVE.md` does not exist
267
+
268
+ Steps:
269
+ 1. create `reqs/REQ-XXXX-<slug>/`
270
+ 2. create `REQUEST.md`
271
+ 3. create `STATUS.md` with status `DRAFT`
272
+ 4. create `reqs/ACTIVE.md` pointing to this requirement with status `DRAFT`
273
+ 5. if sourced from `reqs/INBOX.md`, update that item status to `promoted`
274
+
275
+ ### B) Confirm Requirement (`DRAFT` -> `ACTIVE`)
276
+ Trigger:
277
+ - one of:
278
+ - Full mode: `ROADMAP.md` created in active requirement
279
+ - Mini mode: `REQUEST.md` includes `Mini Roadmap` and meets all Mini criteria
280
+
281
+ Steps:
282
+ 1. choose confirmation mode (`full` or `mini`)
283
+ 2. if `full`: create `ROADMAP.md` (phases, goals, acceptance)
284
+ 3. if `mini`: add `Mini Roadmap` in `REQUEST.md` (tasks + acceptance + criteria flags)
285
+ 4. update `STATUS.md` to `ACTIVE` and record `roadmap_mode: full|mini`
286
+ 5. update `reqs/ACTIVE.md` to `ACTIVE`
287
+
288
+ ### C) Execute Phase (loop)
289
+ For each `PHASE-XX`:
290
+ 1. write `phases/PHASE-XX/PLAN.md` with ordered tasks, validation checklist, exit criteria
291
+ 2. implement according to plan
292
+ 3. append evidence to `phases/PHASE-XX/VALIDATION_LOG.md`
293
+
294
+ ### D) Complete Phase (2 required + 2 conditional)
295
+ A phase is `DONE` only when all required checks pass:
296
+ 1. `VALIDATION_LOG.md` has acceptance conclusion
297
+ 2. `STATUS.md` and `reqs/ACTIVE.md` are updated
298
+ 3. if architecture-impacting changes occurred, ADR is added/updated in `docs/adr/`
299
+ 4. if architecture-impacting changes occurred, `ARCHITECTURE.md` reflects the current design
300
+
301
+ ### E) Pause or Block Current Requirement
302
+ Use when scope split or dependency wait occurs.
303
+
304
+ Steps:
305
+ 1. write checkpoint summary in current `VALIDATION_LOG.md`
306
+ 2. update `STATUS.md` to `PAUSED` or `BLOCKED`
307
+ 3. fill inactive-state record fields (`changed_at`, `reason`, `resume_condition`, `next_action`)
308
+ 4. remove `reqs/ACTIVE.md` to unlock
309
+
310
+ ### F) Switch to Another Requirement
311
+ Precondition:
312
+ - previous requirement is set to `PAUSED` or `BLOCKED`
313
+ - lock is removed
314
+
315
+ Steps:
316
+ 1. execute switch as one atomic operation (no per-step user confirmation)
317
+ 2. activate target by its current status:
318
+ - target `DRAFT`: follow section `B) Confirm Requirement`
319
+ - target `PAUSED`/`BLOCKED`: follow section `G) Resume Paused/Blocked Requirement`
320
+ 3. continue work only on newly active requirement
321
+
322
+ ### G) Resume Paused/Blocked Requirement
323
+ Precondition:
324
+ - `reqs/ACTIVE.md` does not exist
325
+
326
+ Steps:
327
+ 1. verify `resume_condition` is met
328
+ 2. create `reqs/ACTIVE.md` pointing to paused/blocked requirement
329
+ 3. update status to `ACTIVE`
330
+ 4. continue from recorded `next_action`
331
+
332
+ ### H) Finish Requirement (`DONE` + unlock)
333
+ Precondition:
334
+ - all phases are `DONE` and validated
335
+
336
+ Steps:
337
+ 1. update `<active_req_path>/STATUS.md` to `DONE` with date
338
+ 2. remove `reqs/ACTIVE.md`
339
+ 3. optionally append completion summary
340
+
341
+ ### I) Open Urgent Interrupt (Exception Path)
342
+ Precondition:
343
+ - `reqs/ACTIVE.md` exists
344
+ - `reqs/INTERRUPT.md` does not exist
345
+ - interrupt eligibility is satisfied (see Hard Constraints rule 2)
346
+
347
+ Steps:
348
+ 1. append a short checkpoint in current phase `VALIDATION_LOG.md`
349
+ 2. create `reqs/INTERRUPT.md` with interrupt metadata and narrow scope
350
+ 3. execute only interrupt scope and record validation evidence in `reqs/INTERRUPT.md`
351
+ 4. if scope exceeds timebox or becomes non-urgent, close interrupt and move work to normal pause/switch + `REQ-*` flow
352
+
353
+ ### J) Close Urgent Interrupt (Archive + Resume)
354
+ Precondition:
355
+ - `reqs/INTERRUPT.md` exists
356
+
357
+ Steps:
358
+ 1. update `reqs/INTERRUPT.md` with resolution summary and validation outcome
359
+ 2. move `reqs/INTERRUPT.md` to `reqs/interrupts/INT-YYYYMMDD-HHMM-<slug>.md`
360
+ 3. append a single-line resume note in current phase `VALIDATION_LOG.md` with archived interrupt path only
361
+ 4. continue work on requirement referenced by `reqs/ACTIVE.md`
362
+
363
+ Interrupt logging source-of-truth rule:
364
+ - interrupt details (root cause, changes, validation evidence) must live in interrupt record (`reqs/INTERRUPT.md` while active, `reqs/interrupts/INT-*.md` after close)
365
+ - requirement `VALIDATION_LOG.md` must keep only one-line interrupt close/resume note with pointer to archived interrupt record
366
+
367
+ ---
368
+
369
+ ## Backlog Rules (`reqs/INBOX.md` and `DEFERRED.md`)
370
+
371
+ Purpose:
372
+ - capture incoming work without breaking single-active workflow
373
+ - separate independent requests from requirement-local deferred ideas
374
+
375
+ While locked:
376
+ - do not create new requirement docs for side requests
377
+ - append independent/external requests to `reqs/INBOX.md`
378
+ - append requirement-derived/non-mandatory ideas to active requirement `DEFERRED.md`
379
+ - if a request is urgent and satisfies interrupt criteria, handle it via `reqs/INTERRUPT.md` flow
380
+ - capture-time rule: fill all fields from the selected item schema at capture time; if unknown, use explicit placeholder (`unknown` or `-`) and update later
381
+
382
+ `reqs/INBOX.md` item schema (recommended):
383
+
384
+ ```md
385
+ - id: INB-0001
386
+ title: <short request>
387
+ captured_at: 2026-02-14
388
+ source: <who/where>
389
+ priority: P2 # P0 | P1 | P2 | P3
390
+ status: open # open | promoted | dropped
391
+ promoted_to: - # e.g., REQ-0008
392
+ ```
393
+
394
+ Deferred item schema (recommended):
395
+
396
+ ```md
397
+ - id: DEF-0001
398
+ title: <short idea>
399
+ captured_at: 2026-02-14
400
+ reason_deferred: <why not now>
401
+ target: same-req-later # same-req-later | next-req | unknown
402
+ status: open # open | promoted | dropped
403
+ promoted_to: - # e.g., REQ-0008
404
+ ```
405
+
406
+ At requirement close:
407
+ - review all deferred items
408
+ - mark each as `promoted` or `dropped`
409
+ - for `next-req` deferred items, create/update matching `reqs/INBOX.md` item(s)
410
+
411
+ When to execute deferred items:
412
+ 1. at phase planning refresh (check if a deferred item should be pulled into current phase)
413
+ 2. before requirement `DONE` (resolve each deferred item to `promoted` or `dropped`)
414
+ 3. after unlock, when creating next requirement from promoted backlog
415
+
416
+ ---
417
+
418
+ ## File Formats (Machine-Readable)
419
+
420
+ ### `reqs/INBOX.md` (global independent backlog)
421
+
422
+ ```md
423
+ # REQUIREMENT INBOX
424
+
425
+ - id: INB-0001
426
+ title: Add 2FA to login
427
+ captured_at: 2026-02-14
428
+ source: product-review
429
+ priority: P2
430
+ status: open # open | promoted | dropped
431
+ promoted_to: -
432
+ ```
433
+
434
+ ### `reqs/ACTIVE.md` (lock + pointer)
435
+
436
+ ```md
437
+ # ACTIVE REQUIREMENT (LOCK)
438
+
439
+ id: REQ-0007
440
+ path: reqs/REQ-0007-some-feature
441
+ status: ACTIVE # DRAFT | ACTIVE
442
+ current_phase: PHASE-01
443
+ started_at: 2026-02-12
444
+
445
+ links:
446
+ - status: ./REQ-0007-some-feature/STATUS.md
447
+ - request: ./REQ-0007-some-feature/REQUEST.md
448
+ - roadmap: ./REQ-0007-some-feature/ROADMAP.md
449
+ ```
450
+
451
+ ### `reqs/INTERRUPT.md` (active urgent exception)
452
+
453
+ ```md
454
+ # ACTIVE INTERRUPT (EXCEPTION)
455
+
456
+ id: INT-20260214-2300-login-hotfix
457
+ type: HOTFIX # HOTFIX | BLOCKER
458
+ priority: P0 # P0 | P1
459
+ status: ACTIVE # ACTIVE | DONE
460
+ reason: login outage after deployment
461
+ related_active: REQ-0007
462
+ started_at: 2026-02-14T23:00:00Z
463
+ timebox_hours: 4
464
+
465
+ eligibility_check:
466
+ - impact_now: yes
467
+ - timebox_fit: yes
468
+ - scope_safe: yes
469
+
470
+ scope:
471
+ - restore login path
472
+ - add regression test for the failing case
473
+
474
+ validation:
475
+ - <what was checked>
476
+ - <result>
477
+
478
+ exit_rule:
479
+ - on close, move to `reqs/interrupts/INT-*.md` and clear active `reqs/INTERRUPT.md`
480
+ ```
481
+
482
+ ### `STATUS.md` (requirement status)
483
+ Must include:
484
+ - requirement id
485
+ - current status (`DRAFT|ACTIVE|PAUSED|BLOCKED|DONE`)
486
+ - roadmap mode (`full|mini`)
487
+ - phase list (`TODO|ACTIVE|DONE`)
488
+ - pointers to roadmap/current phase plan/validation
489
+ - inactive-state record when status is `PAUSED` or `BLOCKED`
490
+
491
+ ### `REQUEST.md` Mini Roadmap section (when `roadmap_mode: mini`)
492
+
493
+ ```md
494
+ ## Mini Roadmap
495
+ single_phase: yes
496
+ architecture_impact: no
497
+ external_contract_change: no
498
+ data_model_change: no
499
+ security_or_topology_change: no
500
+
501
+ tasks:
502
+ - <ordered task 1>
503
+ - <ordered task 2>
504
+
505
+ acceptance:
506
+ - <validation check 1>
507
+ - <validation check 2>
508
+ ```
509
+
510
+ ### ADR naming
511
+ Use one format:
512
+ - `docs/adr/ADR-0001-<short-title>.md`
513
+ - `docs/adr/ADR-YYYYMMDD-<short-title>.md`
514
+
515
+ Each ADR must reference:
516
+ - related requirement id
517
+ - related phase
518
+ - decision, alternatives, consequences
519
+
520
+ ADR metadata for selective lookup (required for new ADRs):
521
+ - components: `<component-a>, <component-b>, ...`
522
+ - status: `active|superseded`
523
+ - supersedes: `<ADR-id>` (optional)
524
+
525
+ ---
526
+
527
+ ## Prohibitions (MUST NOT)
528
+ - do not create a new requirement while `reqs/ACTIVE.md` exists (except the explicit interrupt exception path)
529
+ - do not treat requirement as confirmed without either:
530
+ - `ROADMAP.md` (full mode), or
531
+ - valid `REQUEST.md` Mini Roadmap that satisfies all Mini criteria (mini mode)
532
+ - do not keep `roadmap_mode: mini` if any Mini criterion becomes false; switch to full mode immediately
533
+ - do not store independent/external requests only in a requirement `DEFERRED.md`
534
+ - do not promote `reqs/INBOX.md` item to a new `REQ-*` while locked, unless pause/switch procedure is completed
535
+ - do not mark a phase `DONE` without:
536
+ - `VALIDATION_LOG` acceptance
537
+ - `STATUS.md` and `ACTIVE.md` update
538
+ - if architecture-impacting changes occurred: ADR update
539
+ - if architecture-impacting changes occurred: `ARCHITECTURE.md` update
540
+ - do not record `architecture_impact: no` when any architecture-impacting criterion is met
541
+ - do not open interrupt work as `reqs/INTERRUPT.md` unless all interrupt eligibility criteria are met
542
+ - do not open interrupt work with any missing gate answers (`impact_now`, `timebox_fit`, `scope_safe`)
543
+ - do not keep more than one active interrupt record
544
+ - do not leave `reqs/INTERRUPT.md` after interrupt close; archive it to `reqs/interrupts/`
545
+ - do not duplicate interrupt detail logs in requirement `VALIDATION_LOG.md`; store details only in interrupt record
546
+ - do not switch requirements without writing pause/block checkpoint + inactive-state record
package/README.md ADDED
@@ -0,0 +1,126 @@
1
+ # ThisIsEnough
2
+
3
+ 가볍지만 실행 가능한 에이전트 작업 규칙을 프로젝트에 설치하는 CLI입니다.
4
+
5
+ 핵심 목표:
6
+ - 요청 수신
7
+ - 로드맵/플랜 정리
8
+ - 페이즈 실행
9
+ - 검증/기록 유지
10
+
11
+ 기본 규칙 문서는 루트 `AGENTS.md`를 사용합니다.
12
+
13
+ ## Install
14
+
15
+ 빠른 시작:
16
+
17
+ ```bash
18
+ npx this-is-enough@latest init --mode existing --dry-run
19
+ npx this-is-enough@latest init --mode existing
20
+ npx this-is-enough@latest doctor
21
+ ```
22
+
23
+ 도움말:
24
+
25
+ ```bash
26
+ npx this-is-enough@latest --help
27
+ ```
28
+
29
+ 로컬 개발 실행:
30
+
31
+ ```bash
32
+ node /path/to/this-is-enough/bin/cli.js --help
33
+ ```
34
+
35
+ ## Commands
36
+
37
+ ### `init`
38
+
39
+ Day-0 구조를 생성/보강합니다.
40
+
41
+ ```bash
42
+ npx this-is-enough@latest init --mode new --cwd /path/to/repo
43
+ npx this-is-enough@latest init --mode existing --cwd /path/to/repo --dry-run
44
+ ```
45
+
46
+ 로컬 실행:
47
+
48
+ ```bash
49
+ node /path/to/this-is-enough/bin/cli.js init --mode existing --cwd /path/to/repo
50
+ ```
51
+
52
+ 옵션:
53
+ - `--mode new|existing`
54
+ - `--cwd <path>` (기본값: 현재 디렉토리)
55
+ - `--dry-run` (변경 예정만 출력)
56
+ - `--force` (기존 파일 덮어쓰기)
57
+ - `--yes` (호환용, 기본이 이미 non-interactive)
58
+
59
+ ### `doctor`
60
+
61
+ 규칙 준수 상태를 점검합니다.
62
+
63
+ ```bash
64
+ npx this-is-enough@latest doctor --cwd /path/to/repo
65
+ ```
66
+
67
+ 로컬 실행:
68
+
69
+ ```bash
70
+ node /path/to/this-is-enough/bin/cli.js doctor --cwd /path/to/repo
71
+ ```
72
+
73
+ 출력:
74
+ - `OK`: 정상
75
+ - `WARN`: 주의
76
+ - `FAIL`: 필수 항목 누락/불일치 (exit code 1)
77
+
78
+ ### `upgrade`
79
+
80
+ 템플릿/구조를 안전하게 업데이트합니다.
81
+
82
+ ```bash
83
+ npx this-is-enough@latest upgrade --cwd /path/to/repo --dry-run
84
+ npx this-is-enough@latest upgrade --cwd /path/to/repo
85
+ ```
86
+
87
+ 로컬 실행:
88
+
89
+ ```bash
90
+ node /path/to/this-is-enough/bin/cli.js upgrade --cwd /path/to/repo
91
+ ```
92
+
93
+ 동작:
94
+ - 누락된 `docs/adr`, `reqs`, `reqs/interrupts` 생성
95
+ - 누락된 `ARCHITECTURE.md`, `reqs/INBOX.md` 생성
96
+ - `AGENTS.md`가 다르면 백업(`AGENTS.md.bak.<timestamp>`) 후 갱신
97
+
98
+ ## Installed Structure
99
+
100
+ `init`/`upgrade`는 아래 구조를 기준으로 맞춥니다.
101
+
102
+ ```text
103
+ ARCHITECTURE.md
104
+ AGENTS.md
105
+ docs/
106
+ adr/
107
+ reqs/
108
+ INBOX.md
109
+ interrupts/
110
+ ```
111
+
112
+ 작업 진행 중에는 규칙에 따라 아래 파일이 생길 수 있습니다.
113
+ - `reqs/ACTIVE.md`
114
+ - `reqs/INTERRUPT.md`
115
+ - `reqs/REQ-XXXX-*/...`
116
+
117
+ ## Workflow Modes
118
+
119
+ - `new`: 빈 프로젝트/초기 프로젝트 설치
120
+ - clean state 전제 (`reqs/ACTIVE.md`, `reqs/INTERRUPT.md` 없음)
121
+ - `existing`: 진행 중 프로젝트 중간 도입
122
+ - 기존 상태 보존 중심으로 설치
123
+
124
+ ## License
125
+
126
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,461 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const HELP = `
7
+ ThisIsEnough CLI
8
+
9
+ Usage:
10
+ this-is-enough init --mode <new|existing> [--cwd <path>] [--dry-run] [--force] [--yes]
11
+ this-is-enough doctor [--cwd <path>]
12
+ this-is-enough upgrade [--cwd <path>] [--dry-run] [--yes]
13
+
14
+ Notes:
15
+ - commands are non-interactive by default (no mid-run prompts).
16
+ - --yes is accepted for compatibility but not required.
17
+ `;
18
+
19
+ const ARCHITECTURE_TEMPLATE = `# ARCHITECTURE.md
20
+
21
+ ## Metadata
22
+ - last_updated:
23
+ - scope: as-is baseline
24
+
25
+ ## System Boundary
26
+ - TODO
27
+ `;
28
+
29
+ const INBOX_TEMPLATE = `# REQUIREMENT INBOX
30
+
31
+ - id: INB-0001
32
+ title: <short request>
33
+ captured_at: YYYY-MM-DD
34
+ source: <who/where>
35
+ priority: P2
36
+ status: open
37
+ promoted_to: -
38
+ `;
39
+
40
+ function resolveProjectPaths(cwd) {
41
+ return {
42
+ agentsPath: path.join(cwd, "AGENTS.md"),
43
+ architecturePath: path.join(cwd, "ARCHITECTURE.md"),
44
+ inboxPath: path.join(cwd, "reqs", "INBOX.md"),
45
+ activePath: path.join(cwd, "reqs", "ACTIVE.md"),
46
+ interruptPath: path.join(cwd, "reqs", "INTERRUPT.md"),
47
+ adrDir: path.join(cwd, "docs", "adr"),
48
+ reqsDir: path.join(cwd, "reqs"),
49
+ interruptsDir: path.join(cwd, "reqs", "interrupts")
50
+ };
51
+ }
52
+
53
+ function fail(message, code = 1) {
54
+ console.error(`ERROR: ${message}`);
55
+ process.exit(code);
56
+ }
57
+
58
+ function info(message) {
59
+ console.log(message);
60
+ }
61
+
62
+ function parseArgs(argv) {
63
+ const args = {
64
+ command: null,
65
+ mode: null,
66
+ cwd: process.cwd(),
67
+ dryRun: false,
68
+ force: false,
69
+ yes: false,
70
+ help: false
71
+ };
72
+
73
+ const tokens = argv.slice(2);
74
+ if (tokens.length === 0) {
75
+ args.help = true;
76
+ return args;
77
+ }
78
+
79
+ if (tokens[0] === "--help" || tokens[0] === "-h") {
80
+ args.help = true;
81
+ return args;
82
+ }
83
+
84
+ args.command = tokens[0];
85
+
86
+ for (let i = 1; i < tokens.length; i += 1) {
87
+ const t = tokens[i];
88
+ if (t === "--help" || t === "-h") {
89
+ args.help = true;
90
+ continue;
91
+ }
92
+ if (t === "--dry-run") {
93
+ args.dryRun = true;
94
+ continue;
95
+ }
96
+ if (t === "--force") {
97
+ args.force = true;
98
+ continue;
99
+ }
100
+ if (t === "--yes") {
101
+ args.yes = true;
102
+ continue;
103
+ }
104
+ if (t === "--mode") {
105
+ const value = tokens[i + 1];
106
+ if (!value) fail("--mode requires a value (new|existing)");
107
+ args.mode = value;
108
+ i += 1;
109
+ continue;
110
+ }
111
+ if (t === "--cwd") {
112
+ const value = tokens[i + 1];
113
+ if (!value) fail("--cwd requires a path");
114
+ args.cwd = path.resolve(value);
115
+ i += 1;
116
+ continue;
117
+ }
118
+ fail(`Unknown option: ${t}`);
119
+ }
120
+
121
+ return args;
122
+ }
123
+
124
+ function ensureDir(dirPath, options, actions) {
125
+ if (fs.existsSync(dirPath)) {
126
+ return;
127
+ }
128
+ actions.push(`mkdir -p ${dirPath}`);
129
+ if (!options.dryRun) {
130
+ fs.mkdirSync(dirPath, { recursive: true });
131
+ }
132
+ }
133
+
134
+ function writeFileIfNeeded(filePath, content, options, actions) {
135
+ if (fs.existsSync(filePath) && !options.force) {
136
+ actions.push(`skip existing ${filePath}`);
137
+ return;
138
+ }
139
+ if (fs.existsSync(filePath) && options.force) {
140
+ actions.push(`overwrite ${filePath}`);
141
+ } else {
142
+ actions.push(`create ${filePath}`);
143
+ }
144
+ if (!options.dryRun) {
145
+ fs.writeFileSync(filePath, content, "utf8");
146
+ }
147
+ }
148
+
149
+ function createFileIfMissing(filePath, content, options, actions) {
150
+ if (fs.existsSync(filePath)) {
151
+ actions.push(`skip existing ${filePath}`);
152
+ return;
153
+ }
154
+ actions.push(`create ${filePath}`);
155
+ if (!options.dryRun) {
156
+ fs.writeFileSync(filePath, content, "utf8");
157
+ }
158
+ }
159
+
160
+ function timestampForBackup() {
161
+ const now = new Date();
162
+ const p = (n) => String(n).padStart(2, "0");
163
+ return `${now.getFullYear()}${p(now.getMonth() + 1)}${p(now.getDate())}-${p(now.getHours())}${p(now.getMinutes())}${p(now.getSeconds())}`;
164
+ }
165
+
166
+ function readTemplateAgents() {
167
+ const templatePath = path.resolve(__dirname, "..", "AGENTS.md");
168
+ if (!fs.existsSync(templatePath)) {
169
+ fail(`Template AGENTS.md not found at ${templatePath}`);
170
+ }
171
+ return fs.readFileSync(templatePath, "utf8");
172
+ }
173
+
174
+ function initProject(options) {
175
+ const mode = options.mode || "existing";
176
+ if (mode !== "new" && mode !== "existing") {
177
+ fail(`Invalid mode "${mode}". Use --mode new or --mode existing.`);
178
+ }
179
+
180
+ if (!fs.existsSync(options.cwd)) {
181
+ fail(`Target directory does not exist: ${options.cwd}`);
182
+ }
183
+
184
+ const actions = [];
185
+ const warnings = [];
186
+ const paths = resolveProjectPaths(options.cwd);
187
+
188
+ const dirs = [
189
+ paths.adrDir,
190
+ paths.reqsDir,
191
+ paths.interruptsDir
192
+ ];
193
+
194
+ for (const dirPath of dirs) {
195
+ ensureDir(dirPath, options, actions);
196
+ }
197
+
198
+ writeFileIfNeeded(paths.agentsPath, readTemplateAgents(), options, actions);
199
+ writeFileIfNeeded(paths.architecturePath, ARCHITECTURE_TEMPLATE, options, actions);
200
+ writeFileIfNeeded(paths.inboxPath, INBOX_TEMPLATE, options, actions);
201
+
202
+ if (mode === "new") {
203
+ if (fs.existsSync(paths.activePath)) {
204
+ fail(`Mode "new" expects clean state, but found ${paths.activePath}`);
205
+ }
206
+ if (fs.existsSync(paths.interruptPath)) {
207
+ fail(`Mode "new" expects clean state, but found ${paths.interruptPath}`);
208
+ }
209
+ } else {
210
+ if (fs.existsSync(paths.activePath)) {
211
+ warnings.push(`existing mode: found active lock ${paths.activePath} (kept as-is)`);
212
+ }
213
+ if (fs.existsSync(paths.interruptPath)) {
214
+ warnings.push(`existing mode: found interrupt lock ${paths.interruptPath} (kept as-is)`);
215
+ }
216
+ }
217
+
218
+ info(`Mode: ${mode}`);
219
+ info(`Target: ${options.cwd}`);
220
+ if (!options.mode) {
221
+ info(`Note: --mode not provided, defaulted to "existing".`);
222
+ }
223
+ if (options.dryRun) {
224
+ info(`Dry run: no files were modified.`);
225
+ }
226
+ if (options.yes) {
227
+ info(`Note: --yes provided (non-interactive mode already default).`);
228
+ }
229
+
230
+ if (actions.length === 0) {
231
+ info(`No changes needed.`);
232
+ } else {
233
+ info(`Planned actions:`);
234
+ for (const action of actions) {
235
+ info(`- ${action}`);
236
+ }
237
+ }
238
+
239
+ if (warnings.length > 0) {
240
+ info(`Warnings:`);
241
+ for (const warning of warnings) {
242
+ info(`- ${warning}`);
243
+ }
244
+ }
245
+
246
+ info(options.dryRun ? "Init check complete." : "Init complete.");
247
+ }
248
+
249
+ function doctorProject(options) {
250
+ if (!fs.existsSync(options.cwd)) {
251
+ fail(`Target directory does not exist: ${options.cwd}`);
252
+ }
253
+
254
+ const paths = resolveProjectPaths(options.cwd);
255
+ const results = [];
256
+
257
+ const addResult = (level, message) => {
258
+ results.push({ level, message });
259
+ };
260
+
261
+ const checkFile = (filePath, label) => {
262
+ if (!fs.existsSync(filePath)) {
263
+ addResult("FAIL", `${label} missing: ${filePath}`);
264
+ return false;
265
+ }
266
+ if (!fs.statSync(filePath).isFile()) {
267
+ addResult("FAIL", `${label} is not a file: ${filePath}`);
268
+ return false;
269
+ }
270
+ addResult("OK", `${label} exists`);
271
+ return true;
272
+ };
273
+
274
+ const checkDir = (dirPath, label) => {
275
+ if (!fs.existsSync(dirPath)) {
276
+ addResult("FAIL", `${label} missing: ${dirPath}`);
277
+ return false;
278
+ }
279
+ if (!fs.statSync(dirPath).isDirectory()) {
280
+ addResult("FAIL", `${label} is not a directory: ${dirPath}`);
281
+ return false;
282
+ }
283
+ addResult("OK", `${label} exists`);
284
+ return true;
285
+ };
286
+
287
+ const hasAgents = checkFile(paths.agentsPath, "AGENTS.md");
288
+ checkFile(paths.architecturePath, "ARCHITECTURE.md");
289
+ checkFile(paths.inboxPath, "reqs/INBOX.md");
290
+ checkDir(paths.adrDir, "docs/adr");
291
+ checkDir(paths.reqsDir, "reqs");
292
+ checkDir(paths.interruptsDir, "reqs/interrupts");
293
+
294
+ if (hasAgents) {
295
+ const agentsText = fs.readFileSync(paths.agentsPath, "utf8");
296
+ if (!agentsText.includes("## Day-0 Bootstrap (MUST)")) {
297
+ addResult("WARN", "AGENTS.md missing Day-0 Bootstrap section");
298
+ } else {
299
+ addResult("OK", "AGENTS.md includes Day-0 Bootstrap section");
300
+ }
301
+ }
302
+
303
+ if (fs.existsSync(paths.activePath)) {
304
+ const activeText = fs.readFileSync(paths.activePath, "utf8");
305
+ addResult("OK", "reqs/ACTIVE.md exists");
306
+
307
+ const statusMatch = activeText.match(/^status:\s*([A-Z]+)\s*$/m);
308
+ if (!statusMatch || (statusMatch[1] !== "DRAFT" && statusMatch[1] !== "ACTIVE")) {
309
+ addResult("FAIL", "reqs/ACTIVE.md status must be DRAFT or ACTIVE");
310
+ } else {
311
+ addResult("OK", `reqs/ACTIVE.md status is ${statusMatch[1]}`);
312
+ }
313
+
314
+ const reqPathMatch = activeText.match(/^path:\s*(.+)\s*$/m);
315
+ if (!reqPathMatch) {
316
+ addResult("FAIL", "reqs/ACTIVE.md missing path field");
317
+ } else {
318
+ const reqPath = path.resolve(options.cwd, reqPathMatch[1].trim());
319
+ if (!fs.existsSync(reqPath)) {
320
+ addResult("FAIL", `active requirement path not found: ${reqPath}`);
321
+ } else {
322
+ addResult("OK", "active requirement path exists");
323
+ }
324
+ }
325
+ } else {
326
+ addResult("OK", "reqs/ACTIVE.md not present (unlocked)");
327
+ }
328
+
329
+ if (fs.existsSync(paths.interruptPath)) {
330
+ const interruptText = fs.readFileSync(paths.interruptPath, "utf8");
331
+ addResult("OK", "reqs/INTERRUPT.md exists");
332
+
333
+ if (!fs.existsSync(paths.activePath)) {
334
+ addResult("FAIL", "reqs/INTERRUPT.md exists without reqs/ACTIVE.md");
335
+ }
336
+
337
+ const requiredGateKeys = ["impact_now", "timebox_fit", "scope_safe"];
338
+ for (const key of requiredGateKeys) {
339
+ const gateMatch = interruptText.match(new RegExp(`^-\\s*${key}:\\s*(yes|no)\\s*$`, "m"));
340
+ if (!gateMatch) {
341
+ addResult("FAIL", `reqs/INTERRUPT.md missing gate answer: ${key}`);
342
+ } else {
343
+ addResult("OK", `interrupt gate present: ${key}=${gateMatch[1]}`);
344
+ }
345
+ }
346
+ } else {
347
+ addResult("OK", "reqs/INTERRUPT.md not present");
348
+ }
349
+
350
+ info(`Target: ${options.cwd}`);
351
+ info("Doctor report:");
352
+ for (const result of results) {
353
+ info(`- ${result.level}: ${result.message}`);
354
+ }
355
+
356
+ const failCount = results.filter((r) => r.level === "FAIL").length;
357
+ const warnCount = results.filter((r) => r.level === "WARN").length;
358
+ const okCount = results.filter((r) => r.level === "OK").length;
359
+ info(`Summary: OK=${okCount} WARN=${warnCount} FAIL=${failCount}`);
360
+
361
+ return failCount === 0 ? 0 : 1;
362
+ }
363
+
364
+ function upgradeProject(options) {
365
+ if (!fs.existsSync(options.cwd)) {
366
+ fail(`Target directory does not exist: ${options.cwd}`);
367
+ }
368
+
369
+ const paths = resolveProjectPaths(options.cwd);
370
+ const actions = [];
371
+ const warnings = [];
372
+
373
+ ensureDir(paths.adrDir, options, actions);
374
+ ensureDir(paths.reqsDir, options, actions);
375
+ ensureDir(paths.interruptsDir, options, actions);
376
+
377
+ createFileIfMissing(paths.architecturePath, ARCHITECTURE_TEMPLATE, options, actions);
378
+ createFileIfMissing(paths.inboxPath, INBOX_TEMPLATE, options, actions);
379
+
380
+ const templateAgents = readTemplateAgents();
381
+ if (!fs.existsSync(paths.agentsPath)) {
382
+ actions.push(`create ${paths.agentsPath}`);
383
+ if (!options.dryRun) {
384
+ fs.writeFileSync(paths.agentsPath, templateAgents, "utf8");
385
+ }
386
+ warnings.push("AGENTS.md did not exist; created from template.");
387
+ } else {
388
+ const currentAgents = fs.readFileSync(paths.agentsPath, "utf8");
389
+ if (currentAgents === templateAgents) {
390
+ actions.push(`skip up-to-date ${paths.agentsPath}`);
391
+ } else {
392
+ const backupPath = `${paths.agentsPath}.bak.${timestampForBackup()}`;
393
+ actions.push(`backup ${paths.agentsPath} -> ${backupPath}`);
394
+ actions.push(`overwrite ${paths.agentsPath}`);
395
+ if (!options.dryRun) {
396
+ fs.copyFileSync(paths.agentsPath, backupPath);
397
+ fs.writeFileSync(paths.agentsPath, templateAgents, "utf8");
398
+ }
399
+ }
400
+ }
401
+
402
+ if (fs.existsSync(paths.activePath)) {
403
+ warnings.push(`active lock detected and kept as-is: ${paths.activePath}`);
404
+ }
405
+ if (fs.existsSync(paths.interruptPath)) {
406
+ warnings.push(`interrupt lock detected and kept as-is: ${paths.interruptPath}`);
407
+ }
408
+
409
+ info(`Target: ${options.cwd}`);
410
+ if (options.dryRun) {
411
+ info("Dry run: no files were modified.");
412
+ }
413
+ if (options.yes) {
414
+ info("Note: --yes provided (non-interactive mode already default).");
415
+ }
416
+
417
+ if (actions.length === 0) {
418
+ info("No changes needed.");
419
+ } else {
420
+ info("Planned actions:");
421
+ for (const action of actions) {
422
+ info(`- ${action}`);
423
+ }
424
+ }
425
+
426
+ if (warnings.length > 0) {
427
+ info("Warnings:");
428
+ for (const warning of warnings) {
429
+ info(`- ${warning}`);
430
+ }
431
+ }
432
+
433
+ info(options.dryRun ? "Upgrade check complete." : "Upgrade complete.");
434
+ return 0;
435
+ }
436
+
437
+ function main() {
438
+ const args = parseArgs(process.argv);
439
+
440
+ if (args.help || !args.command) {
441
+ console.log(HELP.trim());
442
+ process.exit(0);
443
+ }
444
+
445
+ if (args.command === "init") {
446
+ initProject(args);
447
+ return;
448
+ }
449
+
450
+ if (args.command === "doctor") {
451
+ process.exit(doctorProject(args));
452
+ }
453
+
454
+ if (args.command === "upgrade") {
455
+ process.exit(upgradeProject(args));
456
+ }
457
+
458
+ fail(`Unknown command "${args.command}". Supported commands: init, doctor, upgrade.`);
459
+ }
460
+
461
+ main();
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "this-is-enough",
3
+ "version": "0.1.0",
4
+ "description": "Install and bootstrap the ThisIsEnough AGENTS.md workflow.",
5
+ "license": "MIT",
6
+ "bin": {
7
+ "this-is-enough": "bin/cli.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "AGENTS.md"
12
+ ],
13
+ "engines": {
14
+ "node": ">=18"
15
+ }
16
+ }