@vpxa/aikit 0.1.145 → 0.1.147

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.
@@ -230,7 +230,8 @@ If multiple aspects "Need Exploration", spend time re-exploring the codebase bef
230
230
  *
231
231
  * Usage:
232
232
  * node check_staleness.js <handoff-file>
233
- * node check_staleness.js .handoffs/2024-01-15-143022-auth.md
233
+ * node check_staleness.js .aikit-state/handoffs/add-auth/2024-01-15-143022-auth.md
234
+ * node check_staleness.js .aikit-state/handoffs/_standalone/2024-01-15-143022-auth.md
234
235
  */
235
236
 
236
237
  const fs = require('node:fs');
@@ -367,6 +368,15 @@ function calculateStaleness(daysOld, commitsSince, filesChanged, branchMatches,
367
368
  return { level, recommendation, issues };
368
369
  }
369
370
 
371
+ function resolveProjectRootFromHandoff(handoffPath) {
372
+ const handoffsDir = path.dirname(handoffPath);
373
+ const handoffMarkerDir = path.basename(path.dirname(handoffsDir));
374
+ const stateMarkerDir = path.basename(path.dirname(path.dirname(handoffsDir)));
375
+ const isFlowScoped = handoffMarkerDir === 'handoffs' && stateMarkerDir === '.aikit-state';
376
+
377
+ return isFlowScoped ? path.resolve(handoffsDir, '..', '..', '..') : path.resolve(handoffsDir, '..');
378
+ }
379
+
370
380
  function checkStaleness(handoffPath) {
371
381
  if (!fs.existsSync(handoffPath)) return { error: \`Handoff file not found: \${handoffPath}\` };
372
382
 
@@ -374,7 +384,7 @@ function checkStaleness(handoffPath) {
374
384
  const projectPath =
375
385
  meta.projectPath && fs.existsSync(meta.projectPath)
376
386
  ? meta.projectPath
377
- : path.resolve(path.dirname(handoffPath), '..'); // Up from .handoffs/
387
+ : resolveProjectRootFromHandoff(handoffPath);
378
388
 
379
389
  const isGitRepo = runCmd('git rev-parse --git-dir', projectPath).ok;
380
390
 
@@ -475,7 +485,8 @@ function printReport(result) {
475
485
  const args = process.argv.slice(2);
476
486
  if (args.includes('--help') || args.includes('-h') || args.length === 0) {
477
487
  console.log('Usage: node check_staleness.js <handoff-file>');
478
- console.log(' node check_staleness.js .handoffs/2024-01-15-143022-auth.md');
488
+ console.log(' node check_staleness.js .aikit-state/handoffs/<flow-slug>/2024-01-15-143022-auth.md');
489
+ console.log(' node check_staleness.js .aikit-state/handoffs/_standalone/2024-01-15-143022-auth.md');
479
490
  process.exit(args.length === 0 ? 1 : 0);
480
491
  }
481
492
 
@@ -502,6 +513,7 @@ if (args.includes('--json')) {
502
513
  * node create_handoff.js [task-slug]
503
514
  * node create_handoff.js "implementing-auth"
504
515
  * node create_handoff.js "auth-part-2" --continues-from 2024-01-15-auth.md
516
+ * node create_handoff.js "auth-part-2" --flow add-auth
505
517
  * node create_handoff.js # auto-generates slug from timestamp
506
518
  */
507
519
 
@@ -549,48 +561,118 @@ function getGitInfo(projectPath) {
549
561
  return info;
550
562
  }
551
563
 
552
- function findPreviousHandoffs(projectPath) {
553
- const dir = path.join(projectPath, '.handoffs');
554
- if (!fs.existsSync(dir)) return [];
564
+ function getFlowDirectories(projectPath) {
565
+ const flowsDir = path.join(projectPath, '.flows');
566
+ if (!fs.existsSync(flowsDir)) return [];
555
567
 
556
- const handoffs = [];
557
- for (const name of fs.readdirSync(dir)) {
558
- if (!name.endsWith('.md')) continue;
559
- const fp = path.join(dir, name);
568
+ return fs
569
+ .readdirSync(flowsDir)
570
+ .map((name) => ({ name, path: path.join(flowsDir, name) }))
571
+ .filter(({ name, path: flowPath }) => {
572
+ try {
573
+ return !name.startsWith('.') && fs.statSync(flowPath).isDirectory();
574
+ } catch {
575
+ return false;
576
+ }
577
+ })
578
+ .sort((a, b) => {
579
+ try {
580
+ return fs.statSync(b.path).mtimeMs - fs.statSync(a.path).mtimeMs;
581
+ } catch {
582
+ return 0;
583
+ }
584
+ });
585
+ }
586
+
587
+ function getActiveFlowSlug(projectPath) {
588
+ const flowDirs = getFlowDirectories(projectPath);
589
+ if (flowDirs.length === 0) return null;
590
+
591
+ const activeFlow = flowDirs.find(({ path: flowPath }) => {
592
+ const metaPath = path.join(flowPath, 'meta.json');
593
+ if (!fs.existsSync(metaPath)) return false;
560
594
 
561
- let title = name;
562
595
  try {
563
- const content = fs.readFileSync(fp, 'utf-8');
564
- const m = content.match(/^#\\s+(?:Handoff:\\s*)?(.+)$/m);
565
- if (m) title = m[1].trim();
596
+ const meta = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
597
+ return meta.status === 'active';
566
598
  } catch {
567
- /* ignore */
599
+ return false;
568
600
  }
601
+ });
569
602
 
570
- let date = null;
571
- const dm = name.match(/^(\\d{4}-\\d{2}-\\d{2})-(\\d{6})/);
572
- if (dm) {
603
+ return activeFlow ? activeFlow.name : null;
604
+ }
605
+
606
+ function getHandoffsDir(projectPath, flowSlug) {
607
+ if (flowSlug) return path.join(projectPath, '.aikit-state', 'handoffs', flowSlug);
608
+
609
+ const activeFlowSlug = getActiveFlowSlug(projectPath);
610
+ if (activeFlowSlug) return path.join(projectPath, '.aikit-state', 'handoffs', activeFlowSlug);
611
+
612
+ return path.join(projectPath, '.aikit-state', 'handoffs', '_standalone');
613
+ }
614
+
615
+ function findPreviousHandoffs(projectPath, flowSlug) {
616
+ const handoffs = [];
617
+ const baseDir = path.join(projectPath, '.aikit-state', 'handoffs');
618
+
619
+ function collectFromDir(dir, flow) {
620
+ if (!fs.existsSync(dir)) return;
621
+
622
+ for (const name of fs.readdirSync(dir)) {
623
+ if (!name.endsWith('.md')) continue;
624
+ const fp = path.join(dir, name);
625
+
626
+ let title = name;
573
627
  try {
574
- date = new Date(\`\${dm[1]}T\${dm[2].slice(0, 2)}:\${dm[2].slice(2, 4)}:\${dm[2].slice(4, 6)}\`);
628
+ const content = fs.readFileSync(fp, 'utf-8');
629
+ const m = content.match(/^#\\s+(?:Handoff:\\s*)?(.+)$/m);
630
+ if (m) title = m[1].trim();
575
631
  } catch {
576
632
  /* ignore */
577
633
  }
634
+
635
+ let date = null;
636
+ const dm = name.match(/^(\\d{4}-\\d{2}-\\d{2})-(\\d{6})/);
637
+ if (dm) {
638
+ try {
639
+ date = new Date(\`\${dm[1]}T\${dm[2].slice(0, 2)}:\${dm[2].slice(2, 4)}:\${dm[2].slice(4, 6)}\`);
640
+ } catch {
641
+ /* ignore */
642
+ }
643
+ }
644
+
645
+ handoffs.push({ filename: name, path: fp, title, date, flow });
578
646
  }
647
+ }
648
+
649
+ if (!fs.existsSync(baseDir)) return handoffs;
650
+
651
+ const flowDirs = flowSlug
652
+ ? [flowSlug]
653
+ : fs.readdirSync(baseDir).filter((name) => {
654
+ try {
655
+ return fs.statSync(path.join(baseDir, name)).isDirectory();
656
+ } catch {
657
+ return false;
658
+ }
659
+ });
579
660
 
580
- handoffs.push({ filename: name, path: fp, title, date });
661
+ for (const fd of flowDirs) {
662
+ collectFromDir(path.join(baseDir, fd), fd === '_standalone' ? null : fd);
581
663
  }
582
664
 
583
665
  handoffs.sort((a, b) => (b.date || 0) - (a.date || 0));
584
666
  return handoffs;
585
667
  }
586
668
 
587
- function getPreviousHandoffInfo(projectPath, continuesFrom) {
588
- const handoffs = findPreviousHandoffs(projectPath);
669
+ function getPreviousHandoffInfo(projectPath, continuesFrom, flowSlug) {
670
+ const handoffs = findPreviousHandoffs(projectPath, flowSlug);
589
671
 
590
672
  if (continuesFrom) {
591
673
  const found = handoffs.find((h) => h.filename.includes(continuesFrom));
592
674
  return found
593
- ? { exists: true, filename: found.filename, title: found.title }
675
+ ? { exists: true, filename: found.filename, title: found.title, flow: found.flow }
594
676
  : { exists: false, filename: continuesFrom, title: 'Not found' };
595
677
  }
596
678
 
@@ -599,6 +681,7 @@ function getPreviousHandoffInfo(projectPath, continuesFrom) {
599
681
  exists: true,
600
682
  filename: handoffs[0].filename,
601
683
  title: handoffs[0].title,
684
+ flow: handoffs[0].flow,
602
685
  suggested: true,
603
686
  };
604
687
  }
@@ -613,7 +696,7 @@ function sanitizeSlug(slug) {
613
696
  .replace(/[^a-z0-9-]/g, '');
614
697
  }
615
698
 
616
- function generateHandoff(projectPath, slug, continuesFrom) {
699
+ function generateHandoff(projectPath, slug, continuesFrom, flowSlug) {
617
700
  const now = new Date();
618
701
  const timestamp = now.toISOString().replace('T', ' ').slice(0, 19);
619
702
  const fileTs = \`\${now.toISOString().slice(0, 10)}-\${String(now.getHours()).padStart(2, '0')}\${String(now.getMinutes()).padStart(2, '0')}\${String(now.getSeconds()).padStart(2, '0')}\`;
@@ -622,12 +705,13 @@ function generateHandoff(projectPath, slug, continuesFrom) {
622
705
  slug = sanitizeSlug(slug);
623
706
  const filename = \`\${fileTs}-\${slug}.md\`;
624
707
 
625
- const handoffsDir = path.join(projectPath, '.handoffs');
708
+ const resolvedFlowSlug = flowSlug || getActiveFlowSlug(projectPath);
709
+ const handoffsDir = getHandoffsDir(projectPath, flowSlug);
626
710
  fs.mkdirSync(handoffsDir, { recursive: true });
627
711
  const filepath = path.join(handoffsDir, filename);
628
712
 
629
713
  const git = getGitInfo(projectPath);
630
- const prev = getPreviousHandoffInfo(projectPath, continuesFrom);
714
+ const prev = getPreviousHandoffInfo(projectPath, continuesFrom, resolvedFlowSlug);
631
715
 
632
716
  const branchLine = git.branch || '[not a git repo or detached HEAD]';
633
717
 
@@ -651,9 +735,13 @@ function generateHandoff(projectPath, slug, continuesFrom) {
651
735
 
652
736
  let chainSection;
653
737
  if (prev.exists) {
738
+ const currentFlowDir = resolvedFlowSlug || '_standalone';
739
+ const prevFlowDir = prev.flow || '_standalone';
740
+ const prevPath =
741
+ prevFlowDir === currentFlowDir ? \`./\${prev.filename}\` : \`../\${prevFlowDir}/\${prev.filename}\`;
654
742
  chainSection = \`## Handoff Chain
655
743
 
656
- - **Continues from**: [\${prev.filename}](./\${prev.filename})
744
+ - **Continues from**: [\${prev.filename}](\${prevPath})
657
745
  - Previous title: \${prev.title || 'Unknown'}
658
746
  - **Supersedes**: [list any older handoffs this replaces, or "None"]
659
747
 
@@ -758,39 +846,68 @@ function generateHandoff(projectPath, slug, continuesFrom) {
758
846
  \`;
759
847
 
760
848
  fs.writeFileSync(filepath, content, 'utf-8');
761
- return { filepath, filename };
849
+
850
+ const compactContent = \`## Handoff: \${slug}
851
+ Branch: \${branchLine} | Created: \${timestamp}
852
+
853
+ ### State
854
+ [TODO: 1-2 sentences — what's happening, where we left off]
855
+
856
+ ### Decisions
857
+ - [TODO: key decisions with rationale]
858
+
859
+ ### Next Steps
860
+ 1. [TODO: most critical action]
861
+ 2. [TODO: second priority]
862
+
863
+ ### Blockers
864
+ - [TODO: what's blocking progress]
865
+
866
+ ### Assumptions
867
+ - [TODO: assumptions that if wrong, change the approach]\`;
868
+
869
+ return { filepath, filename, compactContent };
762
870
  }
763
871
 
764
872
  // --- Main ---
765
873
  const args = process.argv.slice(2);
766
874
  if (args.includes('--help') || args.includes('-h')) {
767
- console.log('Usage: node create_handoff.js [task-slug] [--continues-from <previous>]');
875
+ console.log('Usage: node create_handoff.js [task-slug] [--continues-from <previous>] [--flow <flow-slug>]');
768
876
  console.log(' node create_handoff.js "implementing-auth"');
769
877
  console.log(' node create_handoff.js "auth-part-2" --continues-from 2024-01-15-auth.md');
878
+ console.log(' node create_handoff.js "auth-part-2" --flow add-auth');
770
879
  process.exit(0);
771
880
  }
772
881
 
773
882
  let slug = null;
774
883
  let continuesFrom = null;
884
+ let flowSlug = null;
775
885
  for (let i = 0; i < args.length; i++) {
776
886
  if (args[i] === '--continues-from' && i + 1 < args.length) {
777
887
  continuesFrom = args[++i];
888
+ } else if (args[i] === '--flow' && i + 1 < args.length) {
889
+ flowSlug = sanitizeSlug(args[++i]);
778
890
  } else if (!args[i].startsWith('-')) {
779
891
  slug = args[i];
780
892
  }
781
893
  }
782
894
 
783
895
  const cwd = process.cwd();
784
- const { filepath } = generateHandoff(cwd, slug, continuesFrom);
896
+ const { filepath, compactContent } = generateHandoff(cwd, slug, continuesFrom, flowSlug);
785
897
  console.log(\`Created handoff: \${filepath}\`);
898
+ console.log('\\nCompact flow knowledge entry:');
899
+ console.log('---');
900
+ console.log(compactContent);
901
+ console.log('---');
786
902
  console.log(\`\\nNext: Open the file and fill in all [TODO: ...] sections.\`);
787
903
  console.log(\`Then validate: node scripts/validate_handoff.js \${filepath}\`);
788
904
  `},{file:`scripts/list_handoffs.js`,content:`#!/usr/bin/env node
789
905
  /**
790
906
  * List available handoff documents in the current project.
791
907
  *
792
- * Searches for handoff documents in .handoffs/ and displays:
908
+ * Searches for handoff documents in .aikit-state/handoffs/<flow-slug>/ and .aikit-state/handoffs/_standalone/ and displays:
793
909
  * - Filename with date
910
+ * - Flow scope
794
911
  * - Title extracted from document
795
912
  * - Status (complete / in progress / needs work)
796
913
  *
@@ -842,23 +959,50 @@ function parseDateFromFilename(filename) {
842
959
  }
843
960
 
844
961
  function listHandoffs(projectPath) {
845
- const dir = path.join(projectPath, '.handoffs');
846
- if (!fs.existsSync(dir)) return [];
847
-
848
962
  const handoffs = [];
849
- for (const name of fs.readdirSync(dir)) {
850
- if (!name.endsWith('.md')) continue;
851
- const fp = path.join(dir, name);
852
- const stat = fs.statSync(fp);
853
-
854
- handoffs.push({
855
- path: fp,
856
- filename: name,
857
- title: extractTitle(fp),
858
- status: checkCompletion(fp),
859
- date: parseDateFromFilename(name),
860
- size: stat.size,
861
- });
963
+ const baseDir = path.join(projectPath, '.aikit-state', 'handoffs');
964
+
965
+ function collectFromDir(dir, flow) {
966
+ if (!fs.existsSync(dir)) return;
967
+
968
+ for (const name of fs.readdirSync(dir)) {
969
+ if (!name.endsWith('.md')) continue;
970
+ const fp = path.join(dir, name);
971
+ const stat = fs.statSync(fp);
972
+
973
+ handoffs.push({
974
+ path: fp,
975
+ filename: name,
976
+ flow,
977
+ title: extractTitle(fp),
978
+ status: checkCompletion(fp),
979
+ date: parseDateFromFilename(name),
980
+ size: stat.size,
981
+ });
982
+ }
983
+ }
984
+
985
+ if (fs.existsSync(baseDir)) {
986
+ const flowDirs = fs
987
+ .readdirSync(baseDir)
988
+ .filter((name) => {
989
+ try {
990
+ return fs.statSync(path.join(baseDir, name)).isDirectory();
991
+ } catch {
992
+ return false;
993
+ }
994
+ })
995
+ .sort((a, b) => {
996
+ try {
997
+ return fs.statSync(path.join(baseDir, b)).mtimeMs - fs.statSync(path.join(baseDir, a)).mtimeMs;
998
+ } catch {
999
+ return 0;
1000
+ }
1001
+ });
1002
+
1003
+ for (const flow of flowDirs) {
1004
+ collectFromDir(path.join(baseDir, flow), flow === '_standalone' ? null : flow);
1005
+ }
862
1006
  }
863
1007
 
864
1008
  handoffs.sort((a, b) => (b.date || 0) - (a.date || 0));
@@ -882,17 +1026,19 @@ const handoffs = listHandoffs(projectPath);
882
1026
 
883
1027
  if (handoffs.length === 0) {
884
1028
  console.log('No handoff documents found.');
885
- console.log(\`Looked in: \${path.join(projectPath, '.handoffs')}\`);
1029
+ console.log(\`Looked in: \${path.join(projectPath, '.aikit-state', 'handoffs', '*')}\`);
1030
+ console.log(\` \${path.join(projectPath, '.aikit-state', 'handoffs', '_standalone')}\`);
886
1031
  console.log('\\nCreate one with: node scripts/create_handoff.js [task-slug]');
887
1032
  process.exit(0);
888
1033
  }
889
1034
 
890
1035
  console.log(\`\\nFound \${handoffs.length} handoff(s) in \${projectPath}:\\n\`);
891
- console.log(\`\${'Date'.padEnd(18)} \${'Status'.padEnd(25)} Title\`);
892
- console.log(\`\${'-'.repeat(18)} \${'-'.repeat(25)} \${'-'.repeat(40)}\`);
1036
+ console.log(\`\${'Date'.padEnd(18)} \${'Flow'.padEnd(18)} \${'Status'.padEnd(25)} Title\`);
1037
+ console.log(\`\${'-'.repeat(18)} \${'-'.repeat(18)} \${'-'.repeat(25)} \${'-'.repeat(40)}\`);
893
1038
 
894
1039
  for (const h of handoffs) {
895
- console.log(\`\${formatDate(h.date).padEnd(18)} \${h.status.padEnd(25)} \${h.title}\`);
1040
+ const flowLabel = h.flow || '[standalone]';
1041
+ console.log(\`\${formatDate(h.date).padEnd(18)} \${flowLabel.padEnd(18)} \${h.status.padEnd(25)} \${h.title}\`);
896
1042
  }
897
1043
 
898
1044
  if (args.includes('--json')) {
@@ -911,7 +1057,8 @@ if (args.includes('--json')) {
911
1057
  *
912
1058
  * Usage:
913
1059
  * node validate_handoff.js <handoff-file>
914
- * node validate_handoff.js .handoffs/2024-01-15-143022-auth.md
1060
+ * node validate_handoff.js .aikit-state/handoffs/add-auth/2024-01-15-143022-auth.md
1061
+ * node validate_handoff.js .aikit-state/handoffs/_standalone/2024-01-15-143022-auth.md
915
1062
  */
916
1063
 
917
1064
  const fs = require('node:fs');
@@ -1039,11 +1186,20 @@ function calculateScore(todosClear, reqComplete, missingReq, missingRec, secrets
1039
1186
  return { score, rating };
1040
1187
  }
1041
1188
 
1189
+ function resolveProjectRootFromHandoff(filepath) {
1190
+ const handoffsDir = path.dirname(filepath);
1191
+ const handoffMarkerDir = path.basename(path.dirname(handoffsDir));
1192
+ const stateMarkerDir = path.basename(path.dirname(path.dirname(handoffsDir)));
1193
+ const isFlowScoped = handoffMarkerDir === 'handoffs' && stateMarkerDir === '.aikit-state';
1194
+
1195
+ return isFlowScoped ? path.resolve(handoffsDir, '..', '..', '..') : path.resolve(handoffsDir, '..');
1196
+ }
1197
+
1042
1198
  function validateHandoff(filepath) {
1043
1199
  if (!fs.existsSync(filepath)) return { error: \`File not found: \${filepath}\` };
1044
1200
 
1045
1201
  const content = fs.readFileSync(filepath, 'utf-8');
1046
- const basePath = path.resolve(path.dirname(filepath), '..'); // Up from .handoffs/
1202
+ const basePath = resolveProjectRootFromHandoff(filepath);
1047
1203
 
1048
1204
  const { clear: todosClear, todos } = checkTodos(content);
1049
1205
  const { complete: reqComplete, missing: missingReq } = checkRequiredSections(content);
@@ -1128,7 +1284,8 @@ function printReport(result) {
1128
1284
  const args = process.argv.slice(2);
1129
1285
  if (args.includes('--help') || args.includes('-h') || args.length === 0) {
1130
1286
  console.log('Usage: node validate_handoff.js <handoff-file>');
1131
- console.log(' node validate_handoff.js .handoffs/2024-01-15-143022-auth.md');
1287
+ console.log(' node validate_handoff.js .aikit-state/handoffs/<flow-slug>/2024-01-15-143022-auth.md');
1288
+ console.log(' node validate_handoff.js .aikit-state/handoffs/_standalone/2024-01-15-143022-auth.md');
1132
1289
  process.exit(args.length === 0 ? 1 : 0);
1133
1290
  }
1134
1291
 
@@ -1147,7 +1304,7 @@ metadata:
1147
1304
  domain: general
1148
1305
  applicability: always
1149
1306
  inputs: [session-state, decisions]
1150
- outputs: [handoff-document]
1307
+ outputs: [handoff-document, flow-knowledge-entry]
1151
1308
  requires: [aikit]
1152
1309
  relatedSkills: [lesson-learned]
1153
1310
  ---
@@ -1169,7 +1326,23 @@ Determine which mode applies:
1169
1326
  **Proactive suggestion?** After substantial work (5+ file edits, complex debugging, major decisions), suggest:
1170
1327
  > "We've made significant progress. Consider creating a handoff document to preserve this context for future sessions. Say 'create handoff' when ready."
1171
1328
 
1172
- > **aikit Integration:** If the project uses the aikit MCP server, complement file-based handoffs with \`knowledge({ action: "remember", title: "Session checkpoint: ...", content: "...", category: "conventions" })\` to persist key decisions in the AI Kit knowledge store. Use \`search({ query: "SESSION CHECKPOINT" })\` at session start to retrieve past checkpoints.
1329
+ > **aikit Integration:** Handoffs use dual storage file in \`.aikit-state/handoffs/{flow-slug}/\` (gitignored, browsable) + compact knowledge entry (\`knowledge({ scope: "flow" })\`). On resume, \`withdraw\` retrieves the compact version automatically. For full context, read the file from \`.aikit-state/\`.
1330
+
1331
+ ## Dual Format Storage
1332
+
1333
+ Each handoff creates TWO artifacts:
1334
+
1335
+ 1. **Full document** → \`.aikit-state/handoffs/{flow-slug}/<timestamp>-<slug>.md\`
1336
+ - Complete template with all sections
1337
+ - Browsable on disk but NOT committed to git
1338
+ - Auto-cleaned when \`.aikit-state/\` is cleared
1339
+
1340
+ 2. **Compact knowledge entry** → \`knowledge({ action: "remember", scope: "flow", category: "session", title: "Session Handoff: <slug>", content: "<compact format>" })\`
1341
+ - ~1-2K chars, optimized for \`withdraw\` retrieval
1342
+ - Contains: State, Decisions, Next Steps, Blockers, Assumptions
1343
+ - Auto-retrieved by resuming agents via \`withdraw({ scope: "flow", profile: "implementer", budget: 6000 })\`
1344
+
1345
+ The compact format ensures continuity even if the next session doesn't explicitly load the full handoff file.
1173
1346
 
1174
1347
  ## CREATE Workflow
1175
1348
 
@@ -1179,6 +1352,7 @@ Run the smart scaffold script to create a pre-filled handoff document:
1179
1352
 
1180
1353
  \`\`\`bash
1181
1354
  node scripts/create_handoff.js [task-slug]
1355
+ node scripts/create_handoff.js [task-slug] --flow <flow-slug>
1182
1356
  \`\`\`
1183
1357
 
1184
1358
  Example: \`node scripts/create_handoff.js implementing-user-auth\`
@@ -1188,12 +1362,16 @@ Example: \`node scripts/create_handoff.js implementing-user-auth\`
1188
1362
  node scripts/create_handoff.js "auth-part-2" --continues-from 2024-01-15-auth.md
1189
1363
  \`\`\`
1190
1364
 
1191
- The script will:
1192
- - Create \`.handoffs/\` directory if needed
1193
- - Generate timestamped filename
1194
- - Pre-fill: timestamp, project path, git branch, recent commits, modified files
1195
- - Add handoff chain links if continuing from previous
1196
- - Output file path for editing
1365
+ The script generates:
1366
+ - Full handoff file in \`.aikit-state/handoffs/{active-flow}/\`
1367
+ - Compact format string (printed to stdout for pasting into \`knowledge({ action: "remember", ... })\`)
1368
+
1369
+ It also:
1370
+ - Uses \`--flow\` to override the active flow slug
1371
+ - Falls back to \`.aikit-state/handoffs/_standalone/\` when no flow is active
1372
+ - Pre-fills: timestamp, project path, git branch, recent commits, modified files
1373
+ - Adds handoff chain links if continuing from previous
1374
+ - Outputs file path for editing
1197
1375
 
1198
1376
  ### Step 2: Complete the Handoff Document
1199
1377
 
@@ -1264,6 +1442,19 @@ The script checks:
1264
1442
  - Branch divergence
1265
1443
  - Missing referenced files
1266
1444
 
1445
+ ### Step 2b: Check Flow Knowledge
1446
+
1447
+ If a flow is active, check for compact handoffs in flow knowledge:
1448
+ \`\`\`
1449
+ knowledge({ action: "withdraw", scope: "flow", profile: "implementer", budget: 6000 })
1450
+ \`\`\`
1451
+
1452
+ If a "Session Handoff:" entry appears but is truncated:
1453
+ \`\`\`
1454
+ knowledge({ action: "list", category: "session", scope: "flow" })
1455
+ knowledge({ action: "read", path: "<handoff-entry>" })
1456
+ \`\`\`
1457
+
1267
1458
  ### Step 3: Load the Handoff
1268
1459
 
1269
1460
  Read the relevant handoff document completely before taking any action.
@@ -1317,7 +1508,10 @@ When resuming from a chain, read the most recent handoff first, then reference p
1317
1508
 
1318
1509
  ## Storage Location
1319
1510
 
1320
- Handoffs are stored in: \`.handoffs/\`
1511
+ **Primary (flow-scoped):** \`.aikit-state/handoffs/{flow-slug}/\`
1512
+ **Standalone fallback:** \`.aikit-state/handoffs/_standalone/\` (when no flow is active)
1513
+
1514
+ All handoffs live in \`.aikit-state/\` which is gitignored — handoffs are ephemeral context, not project history.
1321
1515
 
1322
1516
  Naming convention: \`YYYY-MM-DD-HHMMSS-[slug].md\`
1323
1517
 
@@ -1329,7 +1523,7 @@ Example: \`2024-01-15-143022-implementing-auth.md\`
1329
1523
 
1330
1524
  | Script | Purpose |
1331
1525
  |--------|---------|
1332
- | \`create_handoff.js [slug] [--continues-from <file>]\` | Generate new handoff with smart scaffolding |
1526
+ | \`create_handoff.js [slug] [--continues-from <file>] [--flow <flow-slug>]\` | Generate new handoff with smart scaffolding |
1333
1527
  | \`list_handoffs.js [path]\` | List available handoffs in a project |
1334
1528
  | \`validate_handoff.js <file>\` | Check completeness, quality, and security |
1335
1529
  | \`check_staleness.js <file>\` | Assess if handoff context is still current |