You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

@maccesar/titools

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@maccesar/titools - npm Package Compare versions

Comparing version
2.2.3
to
2.2.4
+83
-96
lib/commands/update.js

@@ -17,3 +17,3 @@ /**

} from '../platform.js';
import { cleanupLegacyArtifacts } from '../cleanup.js';
import { cleanupLegacyArtifacts, getSkillList } from '../cleanup.js';
import {

@@ -26,4 +26,4 @@ installSkills,

import {
downloadRepoArchive,
checkForUpdate,
fetchLatestVersion,
} from '../downloader.js';

@@ -33,8 +33,17 @@ import { createSkillSymlinks } from '../symlink.js';

import { getAgentsSkillsDir } from '../config.js';
import { mkdtemp } from 'fs/promises';
import { existsSync } from 'fs';
import { join, resolve } from 'path';
import { tmpdir } from 'os';
/**
* Check if a platform has any skill symlinks installed
* @param {string} platformSkillsDir - Platform skills directory
* @returns {boolean} True if any skill symlink exists
*/
function hasAnySkillSymlink(platformSkillsDir) {
if (!platformSkillsDir || !existsSync(platformSkillsDir)) return false;
const skillList = getSkillList();
return skillList.some((skill) => existsSync(join(platformSkillsDir, skill)));
}
/**
* Update command handler

@@ -118,118 +127,96 @@ * @param {Object} options - Command options

if (!hasUpdate) {
spinner.info(`Already up to date (v${PACKAGE_VERSION})`);
cleanupLegacyArtifacts(baseDir);
console.log('');
console.log(chalk.green('✓'), 'Skills and agents are already at the latest version');
const projectDir = resolve(process.cwd());
if (isTitaniumProject(projectDir)) {
const aiFiles = ['AGENTS.md', 'CLAUDE.md', 'GEMINI.md'];
const hasAnyAiFile = aiFiles.some((file) => existsSync(join(projectDir, file)));
if (hasAnyAiFile) {
await agentsCommand(projectDir, { onlyExisting: true, force: true });
}
// If there's a newer version on GitHub, prompt user to update CLI first
if (hasUpdate) {
let latestVersion = '(newer)';
try {
latestVersion = await fetchLatestVersion();
} catch {
// Ignore error, we already know there's an update
}
console.log('');
return;
}
spinner.succeed(`Update available!`);
console.log('');
console.log(chalk.gray(`Current: ${PACKAGE_VERSION}`));
console.log(chalk.gray(`Latest: (from GitHub)`));
console.log('');
// Detect installed platforms at target
const detectedPlatforms = detectPlatforms(baseDir);
if (detectedPlatforms.length === 0) {
console.log(chalk.yellow('No AI coding assistants detected.'));
if (baseDir) {
console.log('Update will install skills to ./.agents/skills/');
} else {
console.log('Update will install skills to ~/.agents/skills/');
}
spinner.warn('New version available');
console.log('');
} else {
for (const platform of detectedPlatforms) {
console.log(chalk.green('✓'), platform.displayName);
}
console.log(chalk.yellow('A newer version is available on npm:'));
console.log(` Current: ${chalk.gray('v' + PACKAGE_VERSION)}`);
console.log(` Latest: ${chalk.green(latestVersion)}`);
console.log('');
console.log('To update, run:');
console.log(` ${chalk.cyan('npm update -g @maccesar/titools')}`);
console.log('');
console.log('Then run this command again:');
console.log(` ${chalk.cyan('titools update')}`);
console.log('');
return;
}
// Download latest from GitHub
spinner.start('Downloading latest from GitHub...');
// CLI is up to date, now sync skills from the installed package
spinner.succeed(`CLI is up to date (v${PACKAGE_VERSION})`);
let repoDir = getLocalRepoDir();
let tempDir = null;
// Get repository directory from the installed package
const repoDir = getLocalRepoDir();
if (!repoDir) {
tempDir = await mkdtemp(join(tmpdir(), 'titanium-skills-'));
repoDir = await downloadRepoArchive(tempDir);
console.log('');
console.log(chalk.red('Error: Could not locate skills source directory.'));
console.log('Try reinstalling with:');
console.log(` ${chalk.cyan('npm install -g titools')}`);
return;
}
spinner.succeed('Downloaded from GitHub');
// Detect platforms with existing symlinks (only update those)
const detectedPlatforms = detectPlatforms(baseDir);
const platformsWithSymlinks = detectedPlatforms.filter((p) =>
hasAnySkillSymlink(p.skillsDir)
);
try {
// Install skills
spinner.start('Updating skills...');
const skillsResult = await installSkills(repoDir, baseDir);
spinner.succeed(
`Skills: ${formatList(skillsResult.installed)}`
);
// Install skills
spinner.start('Syncing skills...');
const skillsResult = await installSkills(repoDir, baseDir);
spinner.succeed(`Skills: ${formatList(skillsResult.installed)}`);
// Install agents
spinner.start('Updating agents...');
// Install agents (only if Claude Code has symlinks)
const claudePlatform = platformsWithSymlinks.find((p) => p.name === 'claude');
if (claudePlatform) {
spinner.start('Syncing agents...');
const agentsResult = await installAgents(repoDir, baseDir);
if (agentsResult.installed.length > 0) {
spinner.succeed(
`Agents: ${formatList(agentsResult.installed)}`
);
spinner.succeed(`Agents: ${formatList(agentsResult.installed)}`);
} else {
spinner.info('No agents to update');
spinner.info('No agents to sync');
}
}
cleanupLegacyArtifacts(baseDir);
cleanupLegacyArtifacts(baseDir);
// Update symlinks for detected platforms
for (const platform of detectedPlatforms) {
spinner.start(`Updating ${platform.displayName} symlinks...`);
const symlinkResult = await createSkillSymlinks(
platform.skillsDir,
SKILLS,
baseDir
// Update symlinks only for platforms that already had them
for (const platform of platformsWithSymlinks) {
spinner.start(`Updating ${platform.displayName} symlinks...`);
const symlinkResult = await createSkillSymlinks(
platform.skillsDir,
SKILLS,
baseDir
);
if (symlinkResult.linked.length === SKILLS.length) {
spinner.succeed(`${platform.displayName} linked`);
} else {
spinner.warn(
`${platform.displayName}: ${symlinkResult.linked.length}/${SKILLS.length} linked`
);
if (symlinkResult.linked.length === SKILLS.length) {
spinner.succeed(`${platform.displayName} linked`);
} else {
spinner.warn(
`${platform.displayName}: ${symlinkResult.linked.length}/${SKILLS.length} linked`
);
}
}
}
// Summary
console.log('');
console.log(chalk.green('✓ Update complete!'));
console.log('');
const projectDir = resolve(process.cwd());
if (isTitaniumProject(projectDir)) {
const aiFiles = ['AGENTS.md', 'CLAUDE.md', 'GEMINI.md'];
const hasAnyAiFile = aiFiles.some((file) => existsSync(join(projectDir, file)));
if (hasAnyAiFile) {
await agentsCommand(projectDir, { onlyExisting: true, force: true });
} else {
console.log(chalk.bold('▸'), 'Run in the Titanium project:', chalk.cyan('titools sync'));
}
} else {
console.log(chalk.bold('▸'), 'Run in the Titanium project:', chalk.cyan('titools sync'));
}
console.log('');
// Summary
console.log('');
console.log(chalk.green('✓ Update complete!'));
} finally {
// Clean up temp directory
if (tempDir) {
await import('fs-extra').then(({ remove }) => remove(tempDir));
// Update knowledge index in MD files if in a Titanium project
const projectDir = resolve(process.cwd());
if (isTitaniumProject(projectDir)) {
const aiFiles = ['AGENTS.md', 'CLAUDE.md', 'GEMINI.md'];
const hasAnyAiFile = aiFiles.some((file) => existsSync(join(projectDir, file)));
if (hasAnyAiFile) {
console.log('');
await agentsCommand(projectDir, { onlyExisting: true, force: true });
}
}
console.log('');

@@ -236,0 +223,0 @@ } catch (error) {

{
"name": "@maccesar/titools",
"version": "2.2.3",
"version": "2.2.4",
"description": "Titanium SDK skills and agents for AI coding assistants (Claude Code, Gemini CLI, Codex CLI)",

@@ -5,0 +5,0 @@ "main": "lib/index.js",

@@ -622,9 +622,10 @@ # TiTools - Titanium CLI for AI coding assistants

What it does:
- Checks GitHub for the latest version
- Downloads and installs updated skills and agents
- Updates platform symlinks for all detected platforms
- Cleans up legacy artifacts (`alloy-expert` skill, `ti-researcher` agent)
- Auto-syncs knowledge index files if they exist in the current project
1. Checks GitHub for the latest CLI version
2. If a newer version exists → prompts you to update the CLI first with `npm update -g @maccesar/titools`
3. If CLI is current → syncs skills and agents from the installed package (no download needed)
4. Updates platform symlinks only for platforms that already have them
5. Cleans up legacy artifacts (`alloy-expert` skill, `ti-researcher` agent)
6. Auto-syncs knowledge index files if they exist in the current project
Note: This updates knowledge packages and agents, not the CLI binary itself. To update the CLI, use `npm update -g @maccesar/titools`.
Note: This command syncs knowledge packages and agents from your installed CLI. To get new features, first update the CLI with `npm update -g @maccesar/titools`, then run `titools update`.

@@ -631,0 +632,0 @@ ### titools remove