feat: 实现动态命令前缀检测机制 - 新增 PromptXConfig 工具类统一管理 .promptx 目录配置文件 - 重构 constants.js 为函数式,支持动态命令前缀检测 - init 命令自动保存用户实际使用的命令前缀 - 优先级:环境变量 > 配置文件 > npm环境检测 > 默认值 - 解决 AI 提示命令与用户实际使用不一致的问题 - 完整的 E2E 测试覆盖所有使用场景 核心价值:用户怎么调用 init,AI 就提示相同的命令前缀,确保 AI-First CLI 的用户体验一致性

This commit is contained in:
sean
2025-06-02 10:49:52 +08:00
parent ca9f306c67
commit 268a64a227
4 changed files with 492 additions and 22 deletions

View File

@ -3,30 +3,139 @@
* 统一管理命令格式、路径等配置信息
*/
// 命令前缀配置 - 约定大于配置
export const COMMAND_PREFIX = 'npx dpml-prompt@snapshot'
const PromptXConfig = require('./lib/utils/promptxConfig')
// 常用命令模板
export const COMMANDS = {
INIT: `${COMMAND_PREFIX} init`,
HELLO: `${COMMAND_PREFIX} hello`,
ACTION: `${COMMAND_PREFIX} action`,
LEARN: `${COMMAND_PREFIX} learn`,
RECALL: `${COMMAND_PREFIX} recall`,
REMEMBER: `${COMMAND_PREFIX} remember`,
HELP: `${COMMAND_PREFIX} help`
// 缓存配置实例和命令前缀
let _config = null
let _cachedPrefix = null
/**
* 获取配置实例
*/
function getConfig() {
if (!_config) {
_config = new PromptXConfig()
}
return _config
}
/**
* 动态检测命令前缀
* 优先级:环境变量 > 配置文件 > npm环境变量检测 > 默认值
*/
function detectCommandPrefix() {
// 返回缓存的结果
if (_cachedPrefix) {
return _cachedPrefix
}
// 1. 环境变量优先(用于测试和自定义)
if (process.env.DPML_COMMAND_PREFIX) {
_cachedPrefix = process.env.DPML_COMMAND_PREFIX
return _cachedPrefix
}
// 2. 尝试读取配置文件(同步方式,避免异步复杂性)
try {
const config = getConfig()
const configPath = config.getPath('command-prefix')
const fs = require('fs')
if (fs.existsSync(configPath)) {
_cachedPrefix = fs.readFileSync(configPath, 'utf8').trim()
if (_cachedPrefix) {
return _cachedPrefix
}
}
} catch (error) {
// 忽略读取错误,继续下一步检测
}
// 3. npm环境变量检测
if (process.env.npm_execpath?.includes('npx') ||
process.env.npm_config_user_agent?.includes('npx')) {
_cachedPrefix = 'npx dpml-prompt@snapshot'
} else {
_cachedPrefix = 'npx dpml-prompt@snapshot' // 默认值保持安全
}
return _cachedPrefix
}
/**
* 智能推测用户使用的命令前缀
* 基于环境变量和执行路径的启发式判断
*/
function reconstructCommandPrefix() {
// 最简单最直接的判断:如果有 npm_execpath 且包含 npx就是 npx 调用
if (process.env.npm_execpath && process.env.npm_execpath.includes('npx')) {
return 'npx dpml-prompt@snapshot' // 默认 snapshot 版本
}
// 其他情况默认是全局安装
return 'dpml-prompt'
}
/**
* 保存命令前缀到配置文件
* 在init命令中调用
*/
async function saveCommandPrefix() {
try {
const actualPrefix = reconstructCommandPrefix()
const config = getConfig()
await config.writeText('command-prefix', actualPrefix)
// 更新缓存
_cachedPrefix = actualPrefix
return actualPrefix
} catch (error) {
console.warn('保存命令前缀失败:', error.message)
return null
}
}
// 动态生成命令常量(函数式)
function getCommands() {
const prefix = detectCommandPrefix()
return {
INIT: `${prefix} init`,
HELLO: `${prefix} hello`,
ACTION: `${prefix} action`,
LEARN: `${prefix} learn`,
RECALL: `${prefix} recall`,
REMEMBER: `${prefix} remember`,
HELP: `${prefix} help`
}
}
// 带参数的命令构建函数
export const buildCommand = {
action: (roleId) => `${COMMAND_PREFIX} action ${roleId}`,
learn: (resource) => `${COMMAND_PREFIX} learn ${resource}`,
recall: (query = '') => `${COMMAND_PREFIX} recall${query ? ' ' + query : ''}`,
remember: (content = '<content>') => `${COMMAND_PREFIX} remember${content !== '<content>' ? ' "' + content + '"' : ' <content>'}`
function getBuildCommand() {
const prefix = detectCommandPrefix()
return {
action: (roleId) => `${prefix} action ${roleId}`,
learn: (resource) => `${prefix} learn ${resource}`,
recall: (query = '') => `${prefix} recall${query ? ' ' + query : ''}`,
remember: (content = '<content>') => `${prefix} remember${content !== '<content>' ? ' "' + content + '"' : ' <content>'}`
}
}
// 系统路径配置
export const PATHS = {
// 为了向后兼容,保留原有的静态导出方式
// 但实际上是动态计算的
const COMMANDS = new Proxy({}, {
get(target, prop) {
return getCommands()[prop]
}
})
const buildCommand = new Proxy({}, {
get(target, prop) {
return getBuildCommand()[prop]
}
})
// 系统路径配置(静态)
const PATHS = {
POUCH_DIR: '.promptx',
MEMORY_DIR: '.promptx/memory',
STATE_FILE: '.promptx/pouch.json',
@ -34,10 +143,10 @@ export const PATHS = {
}
// 版本信息
export const VERSION = '0.0.1'
const VERSION = '0.0.1'
// 系统状态
export const STATES = {
const STATES = {
INITIALIZED: 'initialized',
ROLE_DISCOVERY: 'role_discovery',
ACTION_PLAN_GENERATED: 'action_plan_generated',
@ -45,3 +154,31 @@ export const STATES = {
MEMORY_SAVED: 'memory_saved',
RECALL_WAITING: 'recall-waiting'
}
/**
* 清除缓存(主要用于测试)
*/
function clearCache() {
_cachedPrefix = null
_config = null
}
// 导出
module.exports = {
// 新的函数式API推荐
getCommands,
getBuildCommand,
detectCommandPrefix,
reconstructCommandPrefix,
saveCommandPrefix,
clearCache,
// 向后兼容的静态API
COMMANDS,
buildCommand,
// 其他静态常量
PATHS,
VERSION,
STATES
}