@@ -18,6 +18,8 @@ import {
1818 saveStandup ,
1919 readStandup ,
2020 getStandupStreak ,
21+ getLastShipDate ,
22+ getProjectCreationDate ,
2123} from "../storage/local.js" ;
2224import { computeBenchmarks , renderBenchmarks } from "../analytics/benchmarks.js" ;
2325import { getSnoozeWarning } from "../analytics/oracle.js" ;
@@ -59,9 +61,9 @@ export async function trackCommand(options?: {
5961 // ─── --add: Quick task add ────────────────────────────────────
6062 if ( options ?. add ) {
6163 if ( typeof options . add === "string" ) {
62- addTask ( slug , options . add ) ;
64+ await addTask ( slug , options . add ) ;
6365 } else {
64- addTasksViaEditor ( slug ) ;
66+ await addTasksViaEditor ( slug ) ;
6567 }
6668 return ;
6769 }
@@ -118,6 +120,16 @@ export async function trackCommand(options?: {
118120
119121 // ─── Board View ───────────────────────────────────────────────
120122 console . log ( shortcutsHint ( ) ) ;
123+
124+ const lastShip = getLastShipDate ( slug ) ;
125+ const refDate = lastShip || getProjectCreationDate ( slug ) ;
126+ const now = new Date ( ) ;
127+ const diffTime = now . getTime ( ) - refDate . getTime ( ) ;
128+ const diffDays = Math . floor ( diffTime / ( 1000 * 60 * 60 * 24 ) ) ;
129+ if ( diffDays > 14 ) {
130+ console . log ( colors . danger ( ` ⚠️ SHIPPING BLOCK ACTIVE: You haven't shipped in ${ diffDays } days! Task addition is locked unless overridden.\n` ) ) ;
131+ }
132+
121133 console . log ( header ( "This Week" ) ) ;
122134
123135 if ( visibleOpen . length === 0 && done . length === 0 && snoozedActive . length === 0 ) {
@@ -390,9 +402,53 @@ function parseTasks(content: string): ParsedTask[] {
390402 return tasks ;
391403}
392404
405+ async function handleShippingBlock ( slug : string ) : Promise < boolean > {
406+ const lastShip = getLastShipDate ( slug ) ;
407+ const refDate = lastShip || getProjectCreationDate ( slug ) ;
408+ const now = new Date ( ) ;
409+
410+ const diffTime = now . getTime ( ) - refDate . getTime ( ) ;
411+ const diffDays = Math . floor ( diffTime / ( 1000 * 60 * 60 * 24 ) ) ;
412+
413+ if ( diffDays > 14 ) {
414+ console . log ( colors . danger ( `\n⚠️ SHIPPING BLOCK: You haven't shipped in ${ diffDays } days!` ) ) ;
415+ console . log ( colors . muted ( "To maintain momentum, you should ship at least once every 14 days.\n" ) ) ;
416+
417+ const override = await p . confirm ( {
418+ message : "Do you want to override this block and add the task anyway?" ,
419+ active : "Yes, override" ,
420+ inactive : "No, cancel" ,
421+ } ) ;
422+
423+ if ( p . isCancel ( override ) || ! override ) {
424+ console . log ( colors . warning ( "Task addition canceled. Run `loopkit ship` first." ) ) ;
425+ return false ;
426+ }
427+
428+ const reason = await p . text ( {
429+ message : "Please enter a reason for overriding this shipping block:" ,
430+ placeholder : "e.g., waiting on third-party API approval" ,
431+ validate : ( value ) => ( value . trim ( ) . length < 5 ? "Reason must be at least 5 characters." : undefined ) ,
432+ } ) ;
433+
434+ if ( p . isCancel ( reason ) || ! reason ) {
435+ console . log ( colors . warning ( "Task addition canceled. Reason required." ) ) ;
436+ return false ;
437+ }
438+
439+ console . log ( colors . success ( `Override registered. Reason: "${ reason } "` ) ) ;
440+ }
441+ return true ;
442+ }
443+
393444// ─── Task Operations ─────────────────────────────────────────────
394445
395- function addTask ( slug : string , title : string ) : void {
446+ async function addTask ( slug : string , title : string , skipBlockCheck = false ) : Promise < void > {
447+ if ( ! skipBlockCheck ) {
448+ const allowed = await handleShippingBlock ( slug ) ;
449+ if ( ! allowed ) return ;
450+ }
451+
396452 let content = readTasksFile ( slug ) ;
397453 if ( ! content ) {
398454 createTasksScaffold ( slug , slug ) ;
@@ -424,7 +480,10 @@ function addTask(slug: string, title: string): void {
424480 console . log ( pass ( `Added #${ newId } : ${ title } ` ) ) ;
425481}
426482
427- function addTasksViaEditor ( slug : string ) : void {
483+ async function addTasksViaEditor ( slug : string ) : Promise < void > {
484+ const allowed = await handleShippingBlock ( slug ) ;
485+ if ( ! allowed ) return ;
486+
428487 const editor =
429488 process . env . EDITOR ||
430489 process . env . VISUAL ||
@@ -460,7 +519,7 @@ function addTasksViaEditor(slug: string): void {
460519 }
461520
462521 for ( const title of lines ) {
463- addTask ( slug , title ) ;
522+ await addTask ( slug , title , true ) ;
464523 }
465524}
466525
0 commit comments