5-phase-workflow 1.7.0 → 1.7.2

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.
package/bin/install.js CHANGED
@@ -41,7 +41,7 @@ function getInstalledVersion(isGlobal) {
41
41
 
42
42
  try {
43
43
  const data = JSON.parse(fs.readFileSync(versionFile, 'utf8'));
44
- return data.installedVersion;
44
+ return data.packageVersion;
45
45
  } catch (e) {
46
46
  return null; // Corrupted file, treat as missing
47
47
  }
@@ -386,6 +386,20 @@ function selectiveUpdate(targetPath, sourcePath) {
386
386
  log.success('Updated templates/ (workflow files only)');
387
387
  }
388
388
 
389
+ // Ensure .5/.gitignore exists and contains .update-cache.json
390
+ function ensureDotFiveGitignore(dataDir) {
391
+ const gitignorePath = path.join(dataDir, '.gitignore');
392
+ const entry = '.update-cache.json';
393
+ if (fs.existsSync(gitignorePath)) {
394
+ const content = fs.readFileSync(gitignorePath, 'utf8');
395
+ if (!content.includes(entry)) {
396
+ fs.appendFileSync(gitignorePath, '\n' + entry + '\n');
397
+ }
398
+ } else {
399
+ fs.writeFileSync(gitignorePath, entry + '\n');
400
+ }
401
+ }
402
+
389
403
  // Initialize version.json after successful install
390
404
  function initializeVersionJson(isGlobal) {
391
405
  const dataDir = getDataPath(isGlobal);
@@ -400,13 +414,13 @@ function initializeVersionJson(isGlobal) {
400
414
 
401
415
  const versionData = {
402
416
  packageVersion: version,
403
- installedVersion: version,
404
417
  installedAt: now,
405
418
  lastUpdated: now,
406
419
  installationType: isGlobal ? 'global' : 'local'
407
420
  };
408
421
 
409
422
  fs.writeFileSync(versionFile, JSON.stringify(versionData, null, 2));
423
+ ensureDotFiveGitignore(dataDir);
410
424
  log.success('Initialized version tracking');
411
425
  }
412
426
 
@@ -554,26 +568,23 @@ function performUpdate(targetPath, sourcePath, isGlobal, versionInfo) {
554
568
  // Update version.json
555
569
  const dataDir = getDataPath(isGlobal);
556
570
  const versionFile = path.join(dataDir, 'version.json');
571
+ const now = new Date().toISOString();
557
572
 
558
- let versionData;
559
- if (fs.existsSync(versionFile)) {
560
- versionData = JSON.parse(fs.readFileSync(versionFile, 'utf8'));
561
- } else {
562
- // Legacy install, create version.json
563
- versionData = {
564
- installedAt: new Date().toISOString(),
565
- installationType: isGlobal ? 'global' : 'local'
566
- };
567
- }
568
-
569
- versionData.packageVersion = versionInfo.available;
570
- versionData.installedVersion = versionInfo.available;
571
- versionData.lastUpdated = new Date().toISOString();
573
+ const existing = fs.existsSync(versionFile)
574
+ ? JSON.parse(fs.readFileSync(versionFile, 'utf8'))
575
+ : {};
576
+ const versionData = {
577
+ packageVersion: versionInfo.available,
578
+ installedAt: existing.installedAt || now,
579
+ lastUpdated: now,
580
+ installationType: existing.installationType || (isGlobal ? 'global' : 'local')
581
+ };
572
582
 
573
583
  if (!fs.existsSync(dataDir)) {
574
584
  fs.mkdirSync(dataDir, { recursive: true });
575
585
  }
576
586
  fs.writeFileSync(versionFile, JSON.stringify(versionData, null, 2));
587
+ ensureDotFiveGitignore(dataDir);
577
588
 
578
589
  // Create features directory if it doesn't exist
579
590
  const featuresDir = path.join(dataDir, 'features');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "5-phase-workflow",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "A 5-phase feature development workflow for Claude Code",
5
5
  "bin": {
6
6
  "5-phase-workflow": "bin/install.js"
@@ -18,6 +18,7 @@ HARD CONSTRAINTS — violations waste tokens and get blocked by plan-guard:
18
18
  - NEVER describe HOW something will be implemented (file contents, signatures, class structures)
19
19
  - NEVER spawn Task agents with subagent_type other than Explore
20
20
  - NEVER write to any file except .5/features/{name}/feature.md and .5/.planning-active
21
+ - NEVER call EnterPlanMode — the workflow has its own planning process
21
22
  - The feature spec describes WHAT and WHY, never HOW
22
23
  - If you feel the urge to implement, STOP and ask a clarifying question instead
23
24
  - Your output is a SPECIFICATION, not a design document. No code. No file layouts. No API shapes.
@@ -16,6 +16,7 @@ After creating the plan, you are DONE.
16
16
  HARD CONSTRAINTS — violations waste tokens and get blocked by plan-guard:
17
17
  - NEVER write code, pseudo-code, or implementation snippets
18
18
  - NEVER create source files — you create ONE file: plan.md
19
+ - NEVER call EnterPlanMode — the workflow has its own planning process
19
20
  - NEVER spawn Task agents with subagent_type other than Explore
20
21
  - The plan describes WHAT to build and WHERE. Agents figure out HOW by reading existing code.
21
22
  - Each component in the table gets: name, action, file path, one-sentence description, complexity
@@ -40,7 +40,7 @@ async function checkForUpdates(workspaceDir) {
40
40
  }
41
41
 
42
42
  // Compare versions
43
- const installed = versionData.installedVersion;
43
+ const installed = versionData.packageVersion;
44
44
  const latestVersion = await getLatestVersion();
45
45
 
46
46
  let newLatest = null;
@@ -48,16 +48,16 @@ async function checkForUpdates(workspaceDir) {
48
48
  newLatest = latestVersion;
49
49
  }
50
50
 
51
- // Only write if latestAvailableVersion actually changed
52
- const oldLatest = versionData.latestAvailableVersion || null;
51
+ // Read/write latestAvailableVersion from .update-cache.json (gitignored)
52
+ const cacheFile = path.join(path.dirname(versionFile), '.update-cache.json');
53
+ let cacheData = {};
54
+ if (fs.existsSync(cacheFile)) {
55
+ try { cacheData = JSON.parse(fs.readFileSync(cacheFile, 'utf8')); } catch(e) {}
56
+ }
57
+ const oldLatest = cacheData.latestAvailableVersion || null;
53
58
  if (newLatest !== oldLatest) {
54
- versionData.latestAvailableVersion = newLatest;
55
-
56
- // Clean up legacy throttling fields
57
- delete versionData.updateCheckLastRun;
58
- delete versionData.updateCheckFrequency;
59
-
60
- fs.writeFileSync(versionFile, JSON.stringify(versionData, null, 2));
59
+ cacheData.latestAvailableVersion = newLatest;
60
+ fs.writeFileSync(cacheFile, JSON.stringify(cacheData, null, 2));
61
61
  }
62
62
 
63
63
  process.exit(0);
@@ -20,8 +20,8 @@ process.stdin.on('end', () => {
20
20
  const data = JSON.parse(input);
21
21
  const toolName = data.tool_name || '';
22
22
 
23
- // Short-circuit: only check Task, Write, and Edit tools
24
- if (toolName !== 'Task' && toolName !== 'Write' && toolName !== 'Edit') {
23
+ // Short-circuit: only check Task, Write, Edit, and EnterPlanMode tools
24
+ if (toolName !== 'Task' && toolName !== 'Write' && toolName !== 'Edit' && toolName !== 'EnterPlanMode') {
25
25
  process.exit(0);
26
26
  }
27
27
 
@@ -41,6 +41,20 @@ process.stdin.on('end', () => {
41
41
  }
42
42
 
43
43
  // Planning mode enforcement
44
+ if (toolName === 'EnterPlanMode') {
45
+ const blockCount = incrementBlockCount(workspaceDir);
46
+ const escalation = blockCount >= 3
47
+ ? ` WARNING: Block #${blockCount}. Repeated violations. Complete your planning artifact and STOP.`
48
+ : '';
49
+ process.stderr.write(
50
+ `BLOCKED: EnterPlanMode is not allowed during workflow planning phases. ` +
51
+ `The 5-phase workflow has its own planning process. ` +
52
+ `REDIRECT: Continue with your current planning task. ` +
53
+ `Write your output to .5/features/{name}/ and output the completion message when done.${escalation}`
54
+ );
55
+ process.exit(2);
56
+ }
57
+
44
58
  if (toolName === 'Task') {
45
59
  const agentType = toolInput.subagent_type || '';
46
60
  if (agentType && agentType !== 'Explore') {
@@ -50,9 +50,16 @@ process.stdin.on('end', () => {
50
50
  const versionFile = path.join(dir, '.5', 'version.json');
51
51
  const versionData = JSON.parse(fs.readFileSync(versionFile, 'utf8'));
52
52
 
53
- // Update check
54
- const latest = versionData.latestAvailableVersion;
55
- const installed = versionData.installedVersion;
53
+ // Update check — read latestAvailableVersion from cache file (gitignored)
54
+ const cacheFile = path.join(dir, '.5', '.update-cache.json');
55
+ let latest = null;
56
+ if (fs.existsSync(cacheFile)) {
57
+ try {
58
+ const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
59
+ latest = cache.latestAvailableVersion || null;
60
+ } catch(e) {}
61
+ }
62
+ const installed = versionData.packageVersion;
56
63
  if (latest && installed && compareVersions(installed, latest) < 0) {
57
64
  updateIndicator = ` | \x1b[33m↑${latest} → /5:update\x1b[0m`;
58
65
  }
package/src/settings.json CHANGED
@@ -38,7 +38,7 @@
38
38
  ]
39
39
  },
40
40
  {
41
- "matcher": "Task|Write|Edit",
41
+ "matcher": "Task|Write|Edit|EnterPlanMode",
42
42
  "hooks": [
43
43
  {
44
44
  "type": "command",