trinity-method-sdk 2.2.0 → 2.2.1

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/CHANGELOG.md CHANGED
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.2.1] - 2026-02-24
11
+
12
+ ### Added
13
+
14
+ - **Legacy deployment migration in update command** - `trinity update` now detects pre-2.2.0
15
+ `trinity/` deployments at project root and automatically migrates them to `.claude/trinity/`
16
+ (Issue #14)
17
+ - New `migration.ts` module with `detectLegacyDeployment()`, `migrateLegacyDeployment()`,
18
+ and `updateGitignoreForMigration()`
19
+ - Pre-flight checks updated to recognize legacy layouts instead of failing
20
+ - Version detection falls back to `trinity/VERSION` for legacy deployments
21
+ - User knowledge base files preserved during migration
22
+ - **Gitignore migration in update command** - `trinity update` now updates `.gitignore`
23
+ patterns on every run, replacing old `trinity/` entries with current `.claude/trinity/`
24
+ patterns (Issue #14)
25
+
26
+ ### Fixed
27
+
28
+ - **Restored `*CLAUDE.md` to deploy gitignore patterns** - `trinity deploy` now adds
29
+ `*CLAUDE.md` back to `.gitignore`, preventing project-specific CLAUDE.md files from being
30
+ committed to version control (Issue #14)
31
+ - **depcheck false positive for `markdownlint-cli`** - Added `markdownlint-cli` to depcheck
32
+ ignores in CI workflow since it is used by npm scripts, not imported in code
33
+ - **Integration test timeout on Windows CI** - Bumped integration test timeout from 60s to
34
+ 120s in `jest.config.js` for slow Windows runners
35
+
10
36
  ## [2.2.0] - 2026-02-24
11
37
 
12
38
  ### Added
@@ -24,6 +24,7 @@ export async function updateGitignore(spinner) {
24
24
  '# Trinity Method SDK',
25
25
  '.claude/trinity/archive/',
26
26
  '.claude/trinity/templates/',
27
+ '*CLAUDE.md',
27
28
  ];
28
29
  // Check if Trinity section already exists
29
30
  if (!gitignoreContent.includes('# Trinity Method SDK')) {
@@ -10,6 +10,7 @@ import chalk from 'chalk';
10
10
  import inquirer from 'inquirer';
11
11
  import { runUpdatePreflightChecks } from './pre-flight.js';
12
12
  import { detectInstalledSDKVersion } from './version.js';
13
+ import { detectLegacyDeployment, migrateLegacyDeployment, updateGitignoreForMigration, } from './migration.js';
13
14
  import { createUpdateBackup, restoreUserContent, rollbackFromBackup, cleanupBackup, } from './backup.js';
14
15
  import { updateAgents } from './agents.js';
15
16
  import { updateCommands } from './commands.js';
@@ -34,10 +35,23 @@ export async function update(options) {
34
35
  knowledgeBaseUpdated: 0,
35
36
  commandsUpdated: 0,
36
37
  filesUpdated: 0,
38
+ legacyMigrated: false,
39
+ gitignoreUpdated: false,
37
40
  };
38
41
  try {
39
42
  // STEP 1: Pre-flight checks
40
- await runUpdatePreflightChecks(spinner);
43
+ const preflight = await runUpdatePreflightChecks(spinner);
44
+ // STEP 1.5: Legacy migration (pre-2.2.0 trinity/ → .claude/trinity/)
45
+ if (preflight.needsLegacyMigration) {
46
+ const legacyInfo = await detectLegacyDeployment(spinner);
47
+ if (legacyInfo.isLegacy) {
48
+ await migrateLegacyDeployment(spinner);
49
+ stats.legacyMigrated = true;
50
+ }
51
+ }
52
+ // STEP 1.7: Update .gitignore patterns
53
+ const gitignoreChanged = await updateGitignoreForMigration(spinner);
54
+ stats.gitignoreUpdated = gitignoreChanged;
41
55
  // STEP 2: Version check
42
56
  const versionInfo = await detectInstalledSDKVersion(spinner);
43
57
  if (versionInfo.isUpToDate && !options.force) {
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Update Migration Module
3
+ * Handles legacy deployment detection and migration from pre-2.2.0 structure
4
+ * @module cli/commands/update/migration
5
+ */
6
+ import { Ora } from 'ora';
7
+ export interface LegacyInfo {
8
+ isLegacy: boolean;
9
+ legacyVersion: string | null;
10
+ }
11
+ /**
12
+ * Detect if the project has a legacy (pre-2.2.0) Trinity deployment
13
+ * Legacy deployments use `trinity/` at root instead of `.claude/trinity/`
14
+ * @param spinner - ora spinner instance for status display
15
+ * @returns Legacy deployment info
16
+ */
17
+ export declare function detectLegacyDeployment(spinner: Ora): Promise<LegacyInfo>;
18
+ /**
19
+ * Migrate legacy Trinity deployment from `trinity/` to `.claude/trinity/`
20
+ * Preserves user-managed knowledge base files during migration
21
+ * @param spinner - ora spinner instance for status display
22
+ */
23
+ export declare function migrateLegacyDeployment(spinner: Ora): Promise<void>;
24
+ /**
25
+ * Update .gitignore to replace old Trinity patterns with current ones
26
+ * Safe to run on any deployment — idempotent
27
+ * @param spinner - ora spinner instance for status display
28
+ * @returns True if gitignore was updated
29
+ */
30
+ export declare function updateGitignoreForMigration(spinner: Ora): Promise<boolean>;
31
+ //# sourceMappingURL=migration.d.ts.map
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Update Migration Module
3
+ * Handles legacy deployment detection and migration from pre-2.2.0 structure
4
+ * @module cli/commands/update/migration
5
+ */
6
+ import fs from 'fs-extra';
7
+ import path from 'path';
8
+ import { validatePath } from '../../utils/validate-path.js';
9
+ /** Old gitignore patterns from pre-2.2.0 deployments */
10
+ const OLD_GITIGNORE_PATTERNS = ['trinity/', '*CLAUDE.md', 'TRINITY.md'];
11
+ /** Current gitignore patterns */
12
+ const CURRENT_GITIGNORE_PATTERNS = [
13
+ '.claude/trinity/archive/',
14
+ '.claude/trinity/templates/',
15
+ '*CLAUDE.md',
16
+ ];
17
+ /**
18
+ * Detect if the project has a legacy (pre-2.2.0) Trinity deployment
19
+ * Legacy deployments use `trinity/` at root instead of `.claude/trinity/`
20
+ * @param spinner - ora spinner instance for status display
21
+ * @returns Legacy deployment info
22
+ */
23
+ export async function detectLegacyDeployment(spinner) {
24
+ spinner.start('Checking for legacy deployment...');
25
+ const hasLegacyDir = await fs.pathExists('trinity');
26
+ const hasLegacyVersion = await fs.pathExists('trinity/VERSION');
27
+ if (!hasLegacyDir) {
28
+ spinner.info('No legacy deployment detected');
29
+ return { isLegacy: false, legacyVersion: null };
30
+ }
31
+ let legacyVersion = null;
32
+ if (hasLegacyVersion) {
33
+ legacyVersion = (await fs.readFile('trinity/VERSION', 'utf8')).trim();
34
+ }
35
+ spinner.warn(`Legacy deployment detected (v${legacyVersion || 'unknown'})`);
36
+ return { isLegacy: true, legacyVersion };
37
+ }
38
+ /**
39
+ * Migrate legacy Trinity deployment from `trinity/` to `.claude/trinity/`
40
+ * Preserves user-managed knowledge base files during migration
41
+ * @param spinner - ora spinner instance for status display
42
+ */
43
+ export async function migrateLegacyDeployment(spinner) {
44
+ spinner.start('Migrating legacy deployment to .claude/trinity/...');
45
+ // Create new directory structure
46
+ await fs.ensureDir('.claude/trinity');
47
+ await fs.ensureDir('.claude/agents/leadership');
48
+ await fs.ensureDir('.claude/agents/deployment');
49
+ await fs.ensureDir('.claude/agents/audit');
50
+ await fs.ensureDir('.claude/agents/planning');
51
+ await fs.ensureDir('.claude/agents/aj-team');
52
+ await fs.ensureDir('.claude/commands');
53
+ // Move trinity/ contents to .claude/trinity/
54
+ const trinityContents = await fs.readdir('trinity');
55
+ for (const item of trinityContents) {
56
+ const srcPath = path.join('trinity', item);
57
+ const destPath = path.join('.claude/trinity', item);
58
+ // Don't overwrite if destination already exists (prefer new structure)
59
+ if (!(await fs.pathExists(destPath))) {
60
+ await fs.copy(srcPath, destPath);
61
+ }
62
+ }
63
+ // Remove old trinity/ directory
64
+ await fs.remove('trinity');
65
+ spinner.succeed('Legacy deployment migrated to .claude/trinity/');
66
+ }
67
+ /**
68
+ * Update .gitignore to replace old Trinity patterns with current ones
69
+ * Safe to run on any deployment — idempotent
70
+ * @param spinner - ora spinner instance for status display
71
+ * @returns True if gitignore was updated
72
+ */
73
+ export async function updateGitignoreForMigration(spinner) {
74
+ spinner.start('Updating .gitignore patterns...');
75
+ const gitignorePath = '.gitignore';
76
+ if (!(await fs.pathExists(gitignorePath))) {
77
+ spinner.info('No .gitignore found, skipping');
78
+ return false;
79
+ }
80
+ let content = await fs.readFile(gitignorePath, 'utf8');
81
+ const originalContent = content;
82
+ if (content.includes('# Trinity Method SDK')) {
83
+ // Remove the existing Trinity section entirely
84
+ const lines = content.split('\n');
85
+ const filteredLines = [];
86
+ let inTrinitySection = false;
87
+ for (const line of lines) {
88
+ if (line.trim() === '# Trinity Method SDK') {
89
+ inTrinitySection = true;
90
+ continue;
91
+ }
92
+ // End of Trinity section: next comment or blank line after non-Trinity content
93
+ if (inTrinitySection) {
94
+ const trimmed = line.trim();
95
+ // Still in Trinity section if line is empty, or matches known patterns
96
+ if (trimmed === '' ||
97
+ trimmed === 'trinity/' ||
98
+ trimmed === '*CLAUDE.md' ||
99
+ trimmed === 'TRINITY.md' ||
100
+ trimmed === '.claude/trinity/archive/' ||
101
+ trimmed === '.claude/trinity/templates/') {
102
+ continue;
103
+ }
104
+ // Non-Trinity line — we've left the section
105
+ inTrinitySection = false;
106
+ }
107
+ filteredLines.push(line);
108
+ }
109
+ content = filteredLines.join('\n');
110
+ }
111
+ // Remove any standalone old patterns that might exist outside the section
112
+ for (const pattern of OLD_GITIGNORE_PATTERNS) {
113
+ const regex = new RegExp(`^${pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*$`, 'gm');
114
+ content = content.replace(regex, '');
115
+ }
116
+ // Clean up multiple blank lines
117
+ content = content.replace(/\n{3,}/g, '\n\n').trim();
118
+ // Append current Trinity section
119
+ const trinitySection = ['', '# Trinity Method SDK', ...CURRENT_GITIGNORE_PATTERNS].join('\n');
120
+ content = `${content}\n${trinitySection}\n`;
121
+ if (content === originalContent) {
122
+ spinner.info('.gitignore already up to date');
123
+ return false;
124
+ }
125
+ const validatedPath = validatePath(gitignorePath);
126
+ await fs.writeFile(validatedPath, content);
127
+ spinner.succeed('.gitignore patterns updated');
128
+ return true;
129
+ }
130
+ //# sourceMappingURL=migration.js.map
@@ -4,10 +4,15 @@
4
4
  * @module cli/commands/update/pre-flight
5
5
  */
6
6
  import { Ora } from 'ora';
7
+ export interface PreflightResult {
8
+ needsLegacyMigration: boolean;
9
+ }
7
10
  /**
8
11
  * Run pre-flight checks to ensure Trinity Method is deployed
12
+ * Detects both current (.claude/trinity/) and legacy (trinity/) layouts
9
13
  * @param spinner - ora spinner instance for status display
10
- * @throws {UpdateError} If pre-flight checks fail
14
+ * @returns Pre-flight result with migration flags
15
+ * @throws {UpdateError} If no Trinity deployment found at all
11
16
  */
12
- export declare function runUpdatePreflightChecks(spinner: Ora): Promise<void>;
17
+ export declare function runUpdatePreflightChecks(spinner: Ora): Promise<PreflightResult>;
13
18
  //# sourceMappingURL=pre-flight.d.ts.map
@@ -7,31 +7,32 @@ import fs from 'fs-extra';
7
7
  import { UpdateError } from '../../utils/error-classes.js';
8
8
  /**
9
9
  * Run pre-flight checks to ensure Trinity Method is deployed
10
+ * Detects both current (.claude/trinity/) and legacy (trinity/) layouts
10
11
  * @param spinner - ora spinner instance for status display
11
- * @throws {UpdateError} If pre-flight checks fail
12
+ * @returns Pre-flight result with migration flags
13
+ * @throws {UpdateError} If no Trinity deployment found at all
12
14
  */
13
15
  export async function runUpdatePreflightChecks(spinner) {
14
16
  spinner.start('Running pre-flight checks...');
15
- // Check .claude directory exists
16
17
  const claudeExists = await fs.pathExists('.claude');
17
- if (!claudeExists) {
18
- spinner.fail('.claude directory not found');
19
- const { displayInfo } = await import('../../utils/errors.js');
20
- displayInfo('Use: trinity deploy to install');
21
- throw new UpdateError('.claude directory not found', {
22
- reason: 'claude_directory_missing',
23
- });
24
- }
25
- // Check .claude/trinity directory exists
26
18
  const trinityExists = await fs.pathExists('.claude/trinity');
27
- if (!trinityExists) {
28
- spinner.fail('Trinity Method not deployed');
29
- const { displayInfo } = await import('../../utils/errors.js');
30
- displayInfo('Trinity deployment appears incomplete');
31
- throw new UpdateError('Trinity Method not deployed in this project', {
32
- reason: 'trinity_directory_missing',
33
- });
19
+ const legacyExists = await fs.pathExists('trinity');
20
+ // Current structure found — no migration needed
21
+ if (claudeExists && trinityExists) {
22
+ spinner.succeed('Pre-flight checks passed');
23
+ return { needsLegacyMigration: false };
24
+ }
25
+ // Legacy structure found — migration needed
26
+ if (legacyExists) {
27
+ spinner.succeed('Pre-flight checks passed (legacy deployment detected)');
28
+ return { needsLegacyMigration: true };
34
29
  }
35
- spinner.succeed('Pre-flight checks passed');
30
+ // Neither found — not deployed
31
+ spinner.fail('Trinity Method not deployed');
32
+ const { displayInfo } = await import('../../utils/errors.js');
33
+ displayInfo('Use: trinity deploy to install');
34
+ throw new UpdateError('Trinity Method not deployed in this project', {
35
+ reason: 'trinity_directory_missing',
36
+ });
36
37
  }
37
38
  //# sourceMappingURL=pre-flight.js.map
@@ -18,6 +18,12 @@ export function displayUpdateSummary(stats, oldVersion, newVersion) {
18
18
  console.log(chalk.white(` Commands Updated: ${stats.commandsUpdated}`));
19
19
  console.log(chalk.white(` Templates Updated: ${stats.templatesUpdated}`));
20
20
  console.log(chalk.white(` Knowledge Base Updated: ${stats.knowledgeBaseUpdated}`));
21
+ if (stats.legacyMigrated) {
22
+ console.log(chalk.yellow(` Legacy Migration: trinity/ → .claude/trinity/`));
23
+ }
24
+ if (stats.gitignoreUpdated) {
25
+ console.log(chalk.white(` .gitignore: Updated`));
26
+ }
21
27
  console.log(chalk.white(` Total Files Updated: ${stats.agentsUpdated + stats.commandsUpdated + stats.templatesUpdated + stats.knowledgeBaseUpdated}`));
22
28
  console.log('');
23
29
  console.log(chalk.gray(` Version: ${oldVersion} → ${newVersion}\n`));
@@ -9,5 +9,7 @@ export interface UpdateStats {
9
9
  knowledgeBaseUpdated: number;
10
10
  commandsUpdated: number;
11
11
  filesUpdated: number;
12
+ legacyMigrated: boolean;
13
+ gitignoreUpdated: boolean;
12
14
  }
13
15
  //# sourceMappingURL=types.d.ts.map
@@ -13,11 +13,13 @@ import { getPackageJsonPath } from '../../utils/get-sdk-path.js';
13
13
  */
14
14
  export async function detectInstalledSDKVersion(spinner) {
15
15
  spinner.start('Checking versions...');
16
- // Read current version from trinity/VERSION
17
- const versionPath = '.claude/trinity/VERSION';
16
+ // Read current version from .claude/trinity/VERSION (or legacy trinity/VERSION)
18
17
  let currentVersion = '0.0.0';
19
- if (await fs.pathExists(versionPath)) {
20
- currentVersion = (await fs.readFile(versionPath, 'utf8')).trim();
18
+ if (await fs.pathExists('.claude/trinity/VERSION')) {
19
+ currentVersion = (await fs.readFile('.claude/trinity/VERSION', 'utf8')).trim();
20
+ }
21
+ else if (await fs.pathExists('trinity/VERSION')) {
22
+ currentVersion = (await fs.readFile('trinity/VERSION', 'utf8')).trim();
21
23
  }
22
24
  // Read latest version from SDK package.json
23
25
  const sdkPkgPath = await getPackageJsonPath();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trinity-method-sdk",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Trinity Method SDK - Investigation-first development methodology for any project",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",