@relipa/ai-flow-kit 0.1.0-beta.0 → 0.1.1-beta.0
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/README.md +497 -497
- package/bin/aiflow.js +525 -525
- package/bin/ak.js +0 -0
- package/custom/mcp-presets/README.md +0 -0
- package/custom/mcp-presets/backlog.json +0 -0
- package/custom/mcp-presets/figma-desktop.json +0 -0
- package/custom/mcp-presets/figma.json +0 -0
- package/custom/mcp-presets/gitnexus.json +0 -0
- package/custom/mcp-presets/google-sheets.json +0 -0
- package/custom/mcp-presets/jira.json +0 -0
- package/custom/prompts/bug-fix.md +0 -0
- package/custom/prompts/feature.md +0 -0
- package/custom/prompts/investigation.md +0 -0
- package/custom/rules/code-style.md +0 -0
- package/custom/rules/java/code-style.md +0 -0
- package/custom/rules/java/naming.md +0 -0
- package/custom/rules/java/review-checklist.md +0 -0
- package/custom/rules/java/spring-boot-examples.md +0 -0
- package/custom/rules/javascript/code-style.md +0 -0
- package/custom/rules/javascript/naming.md +0 -0
- package/custom/rules/naming.md +0 -0
- package/custom/rules/php/code-style.md +0 -0
- package/custom/rules/php/naming.md +0 -0
- package/custom/rules/project-conventions.md +0 -0
- package/custom/rules/review-checklist.md +0 -0
- package/custom/skills/figma-to-component/SKILL.md +294 -207
- package/custom/skills/generate-spec/SKILL.md +0 -0
- package/custom/skills/impact-analysis/SKILL.md +0 -0
- package/custom/skills/investigate-bug/SKILL.md +0 -0
- package/custom/skills/read-study-requirement/SKILL.md +334 -334
- package/custom/skills/report-customer/SKILL.md +0 -0
- package/custom/skills/review-plan/SKILL.md +0 -0
- package/custom/templates/laravel.md +0 -0
- package/custom/templates/nestjs.md +0 -0
- package/custom/templates/nextjs.md +0 -0
- package/custom/templates/nodejs-express.md +0 -0
- package/custom/templates/php-plain.md +0 -0
- package/custom/templates/php.md +0 -0
- package/custom/templates/python-django.md +0 -0
- package/custom/templates/python-fastapi.md +0 -0
- package/custom/templates/python.md +0 -0
- package/custom/templates/reactjs.md +0 -0
- package/custom/templates/shared/gate-workflow.md +91 -91
- package/custom/templates/spring-boot.md +0 -0
- package/custom/templates/tools/claude.md +0 -0
- package/custom/templates/tools/copilot.md +0 -0
- package/custom/templates/tools/cursor.md +0 -0
- package/custom/templates/tools/gemini.md +0 -0
- package/custom/templates/tools/generic.md +0 -0
- package/custom/templates/vue-nuxt.md +0 -0
- package/docs/common/AIFLOW.md +0 -0
- package/docs/common/CHANGELOG.md +334 -334
- package/docs/common/QUICK_START.md +537 -537
- package/docs/common/ai-integration.md +0 -0
- package/docs/common/cli-reference.md +657 -657
- package/docs/common/configuration.md +0 -0
- package/docs/common/getting-started.md +0 -0
- package/docs/common/troubleshooting.md +0 -0
- package/docs/common/workflows/bug-fix.md +0 -0
- package/docs/common/workflows/feature.md +0 -0
- package/docs/common/workflows/figma.md +0 -0
- package/docs/common/workflows/impact-analysis.md +0 -0
- package/docs/common/workflows/investigation.md +0 -0
- package/docs/common/workflows/refactor.md +0 -0
- package/docs/project/ARCHITECTURE.md +0 -0
- package/index.js +0 -0
- package/package.json +68 -67
- package/scripts/checkpoint.js +0 -0
- package/scripts/config.js +0 -0
- package/scripts/context.js +0 -0
- package/scripts/create-score-excel.js +0 -0
- package/scripts/detect.js +0 -0
- package/scripts/doctor.js +26 -0
- package/scripts/gitnexus-worker.js +0 -0
- package/scripts/guide.js +314 -314
- package/scripts/hooks/block-git-write.js +0 -0
- package/scripts/hooks/session-start.js +315 -315
- package/scripts/hooks/session-stop.js +0 -0
- package/scripts/init.js +0 -0
- package/scripts/link-resolver.js +0 -0
- package/scripts/memory.js +0 -0
- package/scripts/prompt.js +95 -9
- package/scripts/remove.js +0 -0
- package/scripts/score_members.js +320 -0
- package/scripts/task.js +0 -0
- package/scripts/telemetry/cli.js +0 -0
- package/scripts/telemetry/config.js +0 -0
- package/scripts/telemetry/crypto.js +0 -0
- package/scripts/telemetry/flush.js +0 -0
- package/scripts/telemetry/record.js +0 -0
- package/scripts/update.js +0 -0
- package/scripts/use.js +1162 -1162
- package/scripts/validate.js +0 -0
- package/upstream/.claude-plugin/marketplace.json +0 -0
- package/upstream/.claude-plugin/plugin.json +0 -0
- package/upstream/.codex/INSTALL.md +0 -0
- package/upstream/.cursor-plugin/plugin.json +0 -0
- package/upstream/.gitattributes +0 -0
- package/upstream/.github/FUNDING.yml +0 -0
- package/upstream/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- package/upstream/.github/ISSUE_TEMPLATE/config.yml +0 -0
- package/upstream/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- package/upstream/.github/ISSUE_TEMPLATE/platform_support.md +0 -0
- package/upstream/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- package/upstream/.opencode/INSTALL.md +0 -0
- package/upstream/.opencode/plugins/superpowers.js +0 -0
- package/upstream/.version-bump.json +0 -0
- package/upstream/AGENTS.md +0 -0
- package/upstream/CHANGELOG.md +0 -0
- package/upstream/CLAUDE.md +0 -0
- package/upstream/CODE_OF_CONDUCT.md +0 -0
- package/upstream/GEMINI.md +0 -0
- package/upstream/LICENSE +0 -0
- package/upstream/README.md +0 -0
- package/upstream/RELEASE-NOTES.md +0 -0
- package/upstream/agents/code-reviewer.md +0 -0
- package/upstream/commands/brainstorm.md +0 -0
- package/upstream/commands/execute-plan.md +0 -0
- package/upstream/commands/write-plan.md +0 -0
- package/upstream/docs/README.codex.md +0 -0
- package/upstream/docs/README.opencode.md +0 -0
- package/upstream/docs/plans/2025-11-22-opencode-support-design.md +0 -0
- package/upstream/docs/plans/2025-11-22-opencode-support-implementation.md +0 -0
- package/upstream/docs/plans/2025-11-28-skills-improvements-from-user-feedback.md +0 -0
- package/upstream/docs/plans/2026-01-17-visual-brainstorming.md +0 -0
- package/upstream/docs/superpowers/plans/2026-01-22-document-review-system.md +0 -0
- package/upstream/docs/superpowers/plans/2026-02-19-visual-brainstorming-refactor.md +0 -0
- package/upstream/docs/superpowers/plans/2026-03-11-zero-dep-brainstorm-server.md +0 -0
- package/upstream/docs/superpowers/plans/2026-03-23-codex-app-compatibility.md +0 -0
- package/upstream/docs/superpowers/specs/2026-01-22-document-review-system-design.md +0 -0
- package/upstream/docs/superpowers/specs/2026-02-19-visual-brainstorming-refactor-design.md +0 -0
- package/upstream/docs/superpowers/specs/2026-03-11-zero-dep-brainstorm-server-design.md +0 -0
- package/upstream/docs/superpowers/specs/2026-03-23-codex-app-compatibility-design.md +0 -0
- package/upstream/docs/testing.md +0 -0
- package/upstream/docs/windows/polyglot-hooks.md +0 -0
- package/upstream/gemini-extension.json +0 -0
- package/upstream/hooks/hooks-cursor.json +0 -0
- package/upstream/hooks/hooks.json +0 -0
- package/upstream/hooks/run-hook.cmd +0 -0
- package/upstream/hooks/session-start +0 -0
- package/upstream/package.json +0 -0
- package/upstream/scripts/bump-version.sh +0 -0
- package/upstream/skills/brainstorming/SKILL.md +0 -0
- package/upstream/skills/brainstorming/scripts/frame-template.html +0 -0
- package/upstream/skills/brainstorming/scripts/helper.js +0 -0
- package/upstream/skills/brainstorming/scripts/server.cjs +0 -0
- package/upstream/skills/brainstorming/scripts/start-server.sh +0 -0
- package/upstream/skills/brainstorming/scripts/stop-server.sh +0 -0
- package/upstream/skills/brainstorming/spec-document-reviewer-prompt.md +0 -0
- package/upstream/skills/brainstorming/visual-companion.md +0 -0
- package/upstream/skills/dispatching-parallel-agents/SKILL.md +0 -0
- package/upstream/skills/executing-plans/SKILL.md +0 -0
- package/upstream/skills/finishing-a-development-branch/SKILL.md +0 -0
- package/upstream/skills/receiving-code-review/SKILL.md +0 -0
- package/upstream/skills/requesting-code-review/SKILL.md +0 -0
- package/upstream/skills/requesting-code-review/code-reviewer.md +0 -0
- package/upstream/skills/subagent-driven-development/SKILL.md +0 -0
- package/upstream/skills/subagent-driven-development/code-quality-reviewer-prompt.md +0 -0
- package/upstream/skills/subagent-driven-development/implementer-prompt.md +0 -0
- package/upstream/skills/subagent-driven-development/spec-reviewer-prompt.md +0 -0
- package/upstream/skills/systematic-debugging/CREATION-LOG.md +0 -0
- package/upstream/skills/systematic-debugging/SKILL.md +0 -0
- package/upstream/skills/systematic-debugging/condition-based-waiting-example.ts +0 -0
- package/upstream/skills/systematic-debugging/condition-based-waiting.md +0 -0
- package/upstream/skills/systematic-debugging/defense-in-depth.md +0 -0
- package/upstream/skills/systematic-debugging/find-polluter.sh +0 -0
- package/upstream/skills/systematic-debugging/root-cause-tracing.md +0 -0
- package/upstream/skills/systematic-debugging/test-academic.md +0 -0
- package/upstream/skills/systematic-debugging/test-pressure-1.md +0 -0
- package/upstream/skills/systematic-debugging/test-pressure-2.md +0 -0
- package/upstream/skills/systematic-debugging/test-pressure-3.md +0 -0
- package/upstream/skills/tdd-lean/SKILL.md +0 -0
- package/upstream/skills/test-driven-development/SKILL.md +0 -0
- package/upstream/skills/test-driven-development/testing-anti-patterns.md +0 -0
- package/upstream/skills/using-git-worktrees/SKILL.md +0 -0
- package/upstream/skills/using-superpowers/SKILL.md +0 -0
- package/upstream/skills/using-superpowers/references/codex-tools.md +0 -0
- package/upstream/skills/using-superpowers/references/copilot-tools.md +0 -0
- package/upstream/skills/using-superpowers/references/gemini-tools.md +0 -0
- package/upstream/skills/verification-before-completion/SKILL.md +0 -0
- package/upstream/skills/writing-plans/SKILL.md +0 -0
- package/upstream/skills/writing-plans/plan-document-reviewer-prompt.md +0 -0
- package/upstream/skills/writing-skills/SKILL.md +0 -0
- package/upstream/skills/writing-skills/anthropic-best-practices.md +0 -0
- package/upstream/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +0 -0
- package/upstream/skills/writing-skills/graphviz-conventions.dot +0 -0
- package/upstream/skills/writing-skills/persuasion-principles.md +0 -0
- package/upstream/skills/writing-skills/render-graphs.js +0 -0
- package/upstream/skills/writing-skills/testing-skills-with-subagents.md +0 -0
- package/upstream/tests/brainstorm-server/package-lock.json +0 -0
- package/upstream/tests/brainstorm-server/package.json +0 -0
- package/upstream/tests/brainstorm-server/server.test.js +0 -0
- package/upstream/tests/brainstorm-server/windows-lifecycle.test.sh +0 -0
- package/upstream/tests/brainstorm-server/ws-protocol.test.js +0 -0
- package/upstream/tests/claude-code/README.md +0 -0
- package/upstream/tests/claude-code/analyze-token-usage.py +0 -0
- package/upstream/tests/claude-code/run-skill-tests.sh +0 -0
- package/upstream/tests/claude-code/test-document-review-system.sh +0 -0
- package/upstream/tests/claude-code/test-helpers.sh +0 -0
- package/upstream/tests/claude-code/test-subagent-driven-development-integration.sh +0 -0
- package/upstream/tests/claude-code/test-subagent-driven-development.sh +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/action-oriented.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/after-planning-flow.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/claude-suggested-it.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/i-know-what-sdd-means.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/mid-conversation-execute-plan.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/please-use-brainstorming.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/skip-formalities.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/subagent-driven-development-please.txt +0 -0
- package/upstream/tests/explicit-skill-requests/prompts/use-systematic-debugging.txt +0 -0
- package/upstream/tests/explicit-skill-requests/run-all.sh +0 -0
- package/upstream/tests/explicit-skill-requests/run-claude-describes-sdd.sh +0 -0
- package/upstream/tests/explicit-skill-requests/run-extended-multiturn-test.sh +0 -0
- package/upstream/tests/explicit-skill-requests/run-haiku-test.sh +0 -0
- package/upstream/tests/explicit-skill-requests/run-multiturn-test.sh +0 -0
- package/upstream/tests/explicit-skill-requests/run-test.sh +0 -0
- package/upstream/tests/opencode/run-tests.sh +0 -0
- package/upstream/tests/opencode/setup.sh +0 -0
- package/upstream/tests/opencode/test-plugin-loading.sh +0 -0
- package/upstream/tests/opencode/test-priority.sh +0 -0
- package/upstream/tests/opencode/test-tools.sh +0 -0
- package/upstream/tests/skill-triggering/prompts/dispatching-parallel-agents.txt +0 -0
- package/upstream/tests/skill-triggering/prompts/executing-plans.txt +0 -0
- package/upstream/tests/skill-triggering/prompts/requesting-code-review.txt +0 -0
- package/upstream/tests/skill-triggering/prompts/systematic-debugging.txt +0 -0
- package/upstream/tests/skill-triggering/prompts/test-driven-development.txt +0 -0
- package/upstream/tests/skill-triggering/prompts/writing-plans.txt +0 -0
- package/upstream/tests/skill-triggering/run-all.sh +0 -0
- package/upstream/tests/skill-triggering/run-test.sh +0 -0
- package/upstream/tests/subagent-driven-dev/go-fractals/design.md +0 -0
- package/upstream/tests/subagent-driven-dev/go-fractals/plan.md +0 -0
- package/upstream/tests/subagent-driven-dev/go-fractals/scaffold.sh +0 -0
- package/upstream/tests/subagent-driven-dev/run-test.sh +0 -0
- package/upstream/tests/subagent-driven-dev/svelte-todo/design.md +0 -0
- package/upstream/tests/subagent-driven-dev/svelte-todo/plan.md +0 -0
- package/upstream/tests/subagent-driven-dev/svelte-todo/scaffold.sh +0 -0
package/bin/aiflow.js
CHANGED
|
@@ -1,525 +1,525 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const { program } = require('commander');
|
|
4
|
-
const chalk = require('chalk');
|
|
5
|
-
const packageJson = require('../package.json');
|
|
6
|
-
const initCommand = require('../scripts/init');
|
|
7
|
-
const updateCommand = require('../scripts/update');
|
|
8
|
-
const doctorCommand = require('../scripts/doctor');
|
|
9
|
-
const useCommand = require('../scripts/use');
|
|
10
|
-
const TaskDetector = require('../scripts/detect');
|
|
11
|
-
const validateCommand = require('../scripts/validate');
|
|
12
|
-
const memoryCommand = require('../scripts/memory');
|
|
13
|
-
const contextCommand = require('../scripts/context');
|
|
14
|
-
const promptCommand = require('../scripts/prompt');
|
|
15
|
-
const removeCommand = require('../scripts/remove');
|
|
16
|
-
const guideCommand = require('../scripts/guide');
|
|
17
|
-
const telemetryCommand= require('../scripts/telemetry/cli');
|
|
18
|
-
const taskCommand = require('../scripts/task');
|
|
19
|
-
const checkpointCommand = require('../scripts/checkpoint');
|
|
20
|
-
const { record } = require('../scripts/telemetry/record');
|
|
21
|
-
const { updateTaskGateState } = require('../scripts/task');
|
|
22
|
-
|
|
23
|
-
// ── DEPRECATION WARNING ───────────────────────────────────────
|
|
24
|
-
if (!process.env.AIFLOW_IS_AK) {
|
|
25
|
-
console.warn(chalk.yellow('\n⚠ Warning: "aiflow" command is deprecated and will be removed in future versions.'));
|
|
26
|
-
console.warn(chalk.yellow('Please use the shorter "ak" command instead.\n'));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
program
|
|
30
|
-
.version(packageJson.version)
|
|
31
|
-
.description('AI Flow Kit CLI - AI-powered development workflows for teams');
|
|
32
|
-
|
|
33
|
-
// ── GLOBAL TELEMETRY HOOK ────────────────────────────────────
|
|
34
|
-
// We intercept raw argv here so we can catch --version, --help,
|
|
35
|
-
// and invalid commands *before* Commander exits the process.
|
|
36
|
-
// Also alias `-v` to commander's `-V` at the root only — subcommands still
|
|
37
|
-
// keep `-v` as `--verbose`. And alias `--fw` to `--framework`.
|
|
38
|
-
(() => {
|
|
39
|
-
if (process.argv[2] === '-v') process.argv[2] = '-V';
|
|
40
|
-
for (let i = 3; i < process.argv.length; i++) {
|
|
41
|
-
if (process.argv[i] === '--fw') process.argv[i] = '--framework';
|
|
42
|
-
}
|
|
43
|
-
const args = process.argv.slice(2);
|
|
44
|
-
let cmd = 'help';
|
|
45
|
-
if (args.length > 0) {
|
|
46
|
-
let first = args[0];
|
|
47
|
-
|
|
48
|
-
// 1. Phân giải alias cho root commands
|
|
49
|
-
const aliases = {
|
|
50
|
-
'i': 'init',
|
|
51
|
-
'u': 'use',
|
|
52
|
-
'p': 'prompt',
|
|
53
|
-
'd': 'detect',
|
|
54
|
-
't': 'task',
|
|
55
|
-
'ctx': 'context',
|
|
56
|
-
'cp': 'checkpoint',
|
|
57
|
-
'vl': 'validate',
|
|
58
|
-
'mem': 'memory',
|
|
59
|
-
'g': 'guide',
|
|
60
|
-
'rm': 'remove',
|
|
61
|
-
'up': 'update',
|
|
62
|
-
'sync': 'sync-skills',
|
|
63
|
-
'dr': 'doctor',
|
|
64
|
-
'tel': 'telemetry'
|
|
65
|
-
};
|
|
66
|
-
if (aliases[first]) {
|
|
67
|
-
first = aliases[first];
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (['-V', '-v', '--version'].includes(first)) {
|
|
71
|
-
cmd = 'version';
|
|
72
|
-
} else if (['-h', '--help'].includes(first)) {
|
|
73
|
-
cmd = 'help';
|
|
74
|
-
} else if (first.startsWith('-')) {
|
|
75
|
-
cmd = 'unknown_flag';
|
|
76
|
-
if (args.includes('--help') || args.includes('-h')) cmd = 'help';
|
|
77
|
-
} else if (first === 'memory') {
|
|
78
|
-
const sec = args[1];
|
|
79
|
-
// Phân giải alias cho memory subcommands
|
|
80
|
-
let sub = sec;
|
|
81
|
-
if (sec && !sec.startsWith('-')) {
|
|
82
|
-
const memAliases = {
|
|
83
|
-
's': 'save',
|
|
84
|
-
'g': 'get',
|
|
85
|
-
'ls': 'list',
|
|
86
|
-
'sr': 'search',
|
|
87
|
-
'd': 'delete',
|
|
88
|
-
'cl': 'clear'
|
|
89
|
-
};
|
|
90
|
-
if (memAliases[sec]) sub = memAliases[sec];
|
|
91
|
-
cmd = `memory.${sub}`;
|
|
92
|
-
} else {
|
|
93
|
-
cmd = 'memory';
|
|
94
|
-
}
|
|
95
|
-
} else if (first === 'task') {
|
|
96
|
-
const sec = args[1];
|
|
97
|
-
// Phân giải alias subcommand của task
|
|
98
|
-
let sub = sec;
|
|
99
|
-
if (sec && !sec.startsWith('-')) {
|
|
100
|
-
const subAliases = {
|
|
101
|
-
'st': 'status',
|
|
102
|
-
'ls': 'list',
|
|
103
|
-
'p': 'pause',
|
|
104
|
-
'sw': 'switch',
|
|
105
|
-
'r': 'resume',
|
|
106
|
-
'rst': 'reset',
|
|
107
|
-
'rm': 'remove',
|
|
108
|
-
'n': 'next'
|
|
109
|
-
};
|
|
110
|
-
if (subAliases[sec]) sub = subAliases[sec];
|
|
111
|
-
cmd = `task.${sub}`;
|
|
112
|
-
} else {
|
|
113
|
-
cmd = 'task';
|
|
114
|
-
}
|
|
115
|
-
} else {
|
|
116
|
-
cmd = first;
|
|
117
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
118
|
-
cmd = `${first}.help`;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
// Record everything except telemetry and gate actions (gate records its own rich event)
|
|
123
|
-
if (cmd !== 'telemetry' && !cmd.startsWith('telemetry.') && cmd !== 'gate') {
|
|
124
|
-
record('command.invoked', { command: cmd });
|
|
125
|
-
}
|
|
126
|
-
})();
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// ── init ──────────────────────────────────────────────────────
|
|
130
|
-
program
|
|
131
|
-
.command('init')
|
|
132
|
-
.alias('i')
|
|
133
|
-
.description('Initialize AI Flow Kit in your project')
|
|
134
|
-
.option('-f, --framework <types>', 'framework(s), comma-separated (e.g. spring-boot,reactjs)')
|
|
135
|
-
.option('-a, --adapter <types>', 'adapter(s), comma-separated (e.g. backlog,jira)')
|
|
136
|
-
.option('-e, --env <types>', 'AI environment(s)/tool(s), comma-separated (e.g. cursor,gemini,copilot)')
|
|
137
|
-
.option('--with-rtk', 'force enable RTK token compression hook')
|
|
138
|
-
.option('--no-rtk', 'skip RTK setup even if RTK is detected')
|
|
139
|
-
.action((options) => {
|
|
140
|
-
options.frameworks = options.framework
|
|
141
|
-
? options.framework.split(',').map(s => s.trim()).filter(Boolean)
|
|
142
|
-
: [];
|
|
143
|
-
options.adapters = options.adapter
|
|
144
|
-
? options.adapter.split(',').map(s => s.trim()).filter(Boolean)
|
|
145
|
-
: [];
|
|
146
|
-
options.aiTools = options.env
|
|
147
|
-
? options.env.split(',').map(s => s.trim()).filter(Boolean)
|
|
148
|
-
: Object.keys(require('../scripts/init').AI_TOOL_FILES || {}).filter(t => t !== 'generic');
|
|
149
|
-
|
|
150
|
-
initCommand(options);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
// ── use ───────────────────────────────────────────────────────
|
|
154
|
-
program
|
|
155
|
-
.command('use [targets...]')
|
|
156
|
-
.alias('u')
|
|
157
|
-
.description('Load context from ticket(s), version, or file(s). First target is primary; rest become supplementary context.')
|
|
158
|
-
.option('-m, --manual', 'manual context entry')
|
|
159
|
-
.option('-f, --file <path>', 'load from file')
|
|
160
|
-
.option('-s, --save <name>', 'save as named context')
|
|
161
|
-
.option('-c, --coms', 'load all comments (alias for --with-comments)')
|
|
162
|
-
.option('--with-comments', 'load all comments')
|
|
163
|
-
.option('--cid <id>', 'load a specific comment by ID')
|
|
164
|
-
.option('--clast <n>', 'load the last N comments', parseInt)
|
|
165
|
-
.option('--cfrom <id>', 'load comments from ID N onward', parseInt)
|
|
166
|
-
.option('--cto <id>', 'load comments up to ID N', parseInt)
|
|
167
|
-
.option('-F, --fast', 'fast mode: minimal Q&A, concise requirement doc (Default)')
|
|
168
|
-
.option('-U, --full', 'full mode: force complete analysis with Q&A (default)')
|
|
169
|
-
.addHelpText('after', `
|
|
170
|
-
Examples:
|
|
171
|
-
$ ak use PROJ-33 Single ticket (primary only)
|
|
172
|
-
$ ak use PROJ-33 PROJ-10 docs/arch.md Multi-target: PROJ-33 primary, rest supplementary (v0.1.0+)
|
|
173
|
-
$ ak use https://company.backlog.com/view/PROJ-33 Load by Backlog URL
|
|
174
|
-
$ ak use PROJ-33 -c Include all comments
|
|
175
|
-
$ ak use PROJ-33 --clast 5 Newest 5 comments only
|
|
176
|
-
$ ak use --file task.md Load from local file
|
|
177
|
-
$ ak use --manual Manual entry
|
|
178
|
-
|
|
179
|
-
Auto link resolution (v0.1.0+):
|
|
180
|
-
Backlog/Jira URLs in the primary ticket description are auto-fetched and added
|
|
181
|
-
to supplementaryContext[]. Comment links (#comment-456 or ?focusedCommentId=456)
|
|
182
|
-
fetch only that comment. Capped at 5 auto-resolved links per call.
|
|
183
|
-
|
|
184
|
-
Related:
|
|
185
|
-
ak fetch-links <url> Fetch a single Backlog/Jira link → JSON (AI runtime use)
|
|
186
|
-
`)
|
|
187
|
-
.action((targets, options) => {
|
|
188
|
-
useCommand(targets, options);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
// ── fetch-links ───────────────────────────────────────────────
|
|
192
|
-
program
|
|
193
|
-
.command('fetch-links <url>')
|
|
194
|
-
.description('Fetch a backlog/jira link and output SupplementaryContext JSON to stdout. Used by AI at runtime.')
|
|
195
|
-
.addHelpText('after', `
|
|
196
|
-
Examples:
|
|
197
|
-
$ ak fetch-links "https://company.backlog.com/view/PROJ-10"
|
|
198
|
-
$ ak fetch-links "https://company.backlog.com/view/PROJ-10#comment-456"
|
|
199
|
-
$ ak fetch-links "https://company.atlassian.net/browse/PROJ-10"
|
|
200
|
-
$ ak fetch-links "https://company.atlassian.net/browse/PROJ-10?focusedCommentId=789"
|
|
201
|
-
|
|
202
|
-
Exit codes:
|
|
203
|
-
0 Success — JSON written to stdout
|
|
204
|
-
1 URL not recognised, or fetch failed (error on stderr)
|
|
205
|
-
`)
|
|
206
|
-
.action(async (url) => {
|
|
207
|
-
const { fetchLink } = require('../scripts/link-resolver');
|
|
208
|
-
const { loadCredentials } = require('../scripts/use');
|
|
209
|
-
const credentials = await loadCredentials();
|
|
210
|
-
try {
|
|
211
|
-
const result = await fetchLink(url, credentials);
|
|
212
|
-
if (!result) {
|
|
213
|
-
process.stderr.write(`Not a recognized backlog/jira URL: ${url}\n`);
|
|
214
|
-
process.exit(1);
|
|
215
|
-
}
|
|
216
|
-
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
217
|
-
} catch (err) {
|
|
218
|
-
process.stderr.write(`Error fetching link: ${err.message}\n`);
|
|
219
|
-
process.exit(1);
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
// ── prompt ────────────────────────────────────────────────────
|
|
224
|
-
program
|
|
225
|
-
.command('prompt [type]')
|
|
226
|
-
.alias('p')
|
|
227
|
-
.description('Generate AI prompt with context and rules')
|
|
228
|
-
.option('-l, --list', 'list available prompt types')
|
|
229
|
-
.option('-o, --output <file>', 'save to file')
|
|
230
|
-
.option('-L, --lang <lang>', 'language: english (default) or vietnamese')
|
|
231
|
-
.option('-d, --detail <level>', 'detail level: minimal | standard (default) | comprehensive')
|
|
232
|
-
.action((type, options) => {
|
|
233
|
-
promptCommand(type, options);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
// ── detect ────────────────────────────────────────────────────
|
|
237
|
-
program
|
|
238
|
-
.command('detect [description]')
|
|
239
|
-
.alias('d')
|
|
240
|
-
.description('Auto-detect task type from description')
|
|
241
|
-
.option('-v, --verbose', 'show detection details')
|
|
242
|
-
.option('-t, --threshold <number>', 'confidence threshold (0-100)')
|
|
243
|
-
.action((description, options) => {
|
|
244
|
-
const detector = new TaskDetector();
|
|
245
|
-
const threshold = options.threshold ? parseInt(options.threshold) / 100 : 0.8;
|
|
246
|
-
const result = detector.detect(description, threshold);
|
|
247
|
-
console.log(chalk.cyan('\nTask Detection Results:\n'));
|
|
248
|
-
console.log(detector.formatOutput(result, options.verbose));
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
// ── task ─────────────────────────────────────────────────────
|
|
252
|
-
const taskCmd = program.command('task').alias('t').description('Manage multiple tasks — pause, switch, resume');
|
|
253
|
-
|
|
254
|
-
taskCmd
|
|
255
|
-
.command('status')
|
|
256
|
-
.alias('st')
|
|
257
|
-
.description('Show active task and pending tasks')
|
|
258
|
-
.action(() => { taskCommand('status'); });
|
|
259
|
-
|
|
260
|
-
taskCmd
|
|
261
|
-
.command('list')
|
|
262
|
-
.alias('ls')
|
|
263
|
-
.description('List all saved tasks')
|
|
264
|
-
.action(() => { taskCommand('list'); });
|
|
265
|
-
|
|
266
|
-
taskCmd
|
|
267
|
-
.command('pause')
|
|
268
|
-
.alias('p')
|
|
269
|
-
.description('Pause current task and save progress')
|
|
270
|
-
.option('-n, --note <text>', 'note about why you are pausing')
|
|
271
|
-
.action((opts) => { taskCommand('pause', { note: opts.note }); });
|
|
272
|
-
|
|
273
|
-
taskCmd
|
|
274
|
-
.command('switch <ticket-id>')
|
|
275
|
-
.alias('sw')
|
|
276
|
-
.description('Pause current task and switch to another')
|
|
277
|
-
.action((ticketId) => { taskCommand('switch', { taskId: ticketId }); });
|
|
278
|
-
|
|
279
|
-
taskCmd
|
|
280
|
-
.command('resume <ticket-id>')
|
|
281
|
-
.alias('r')
|
|
282
|
-
.description('Resume a paused task')
|
|
283
|
-
.action((ticketId) => { taskCommand('resume', { taskId: ticketId }); });
|
|
284
|
-
|
|
285
|
-
taskCmd
|
|
286
|
-
.command('reset <ticket-id>')
|
|
287
|
-
.alias('rst')
|
|
288
|
-
.description('Reset task to Gate 1 (keeps ticket context, clears gate progress)')
|
|
289
|
-
.action((ticketId) => { taskCommand('reset', { taskId: ticketId }); });
|
|
290
|
-
|
|
291
|
-
taskCmd
|
|
292
|
-
.command('remove <ticket-id>')
|
|
293
|
-
.alias('rm')
|
|
294
|
-
.description('Permanently delete all saved data for a task')
|
|
295
|
-
.action((ticketId) => { taskCommand('remove', { taskId: ticketId }); });
|
|
296
|
-
|
|
297
|
-
taskCmd
|
|
298
|
-
.command('next')
|
|
299
|
-
.alias('n')
|
|
300
|
-
.description('Approve current gate and prepare next session (saves state for resume)')
|
|
301
|
-
.option('-t, --ticket <id>', 'ticket ID (defaults to active task)')
|
|
302
|
-
.action((opts) => {
|
|
303
|
-
taskCommand('next', { taskId: opts.ticket });
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
// ── context ───────────────────────────────────────────────────
|
|
307
|
-
program
|
|
308
|
-
.command('context [action]')
|
|
309
|
-
.alias('ctx')
|
|
310
|
-
.description('Manage task contexts (list|show|save <name>|clear)')
|
|
311
|
-
.option('-s, --save <name>', 'save current context with name')
|
|
312
|
-
.option('-l, --load <name>', 'load saved context by name')
|
|
313
|
-
.option('-d, --delete <name>', 'delete saved context')
|
|
314
|
-
.option('--clear', 'clear all saved contexts')
|
|
315
|
-
.option('--show', 'show current context')
|
|
316
|
-
.action((action, options) => {
|
|
317
|
-
contextCommand(action, options);
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
// ── checkpoint ────────────────────────────────────────────────
|
|
321
|
-
program
|
|
322
|
-
.command('checkpoint')
|
|
323
|
-
.alias('cp')
|
|
324
|
-
.description('Record token usage checkpoint (called by AI during gate work)')
|
|
325
|
-
.requiredOption('-g, --gate <n>', 'gate number (1–5)', parseInt)
|
|
326
|
-
.requiredOption('-s, --step <name>', 'step name (e.g. "tests-written")')
|
|
327
|
-
.requiredOption('-n, --tokens <n>', 'estimated token count', parseInt)
|
|
328
|
-
.option('-t, --ticket <id>', 'ticket ID (defaults to active task)')
|
|
329
|
-
.action((opts) => {
|
|
330
|
-
checkpointCommand({ gate: opts.gate, step: opts.step, tokens: opts.tokens, ticket: opts.ticket });
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
// ── validate ──────────────────────────────────────────────────
|
|
334
|
-
program
|
|
335
|
-
.command('validate <file>')
|
|
336
|
-
.alias('vl')
|
|
337
|
-
.description('Validate output against team rules')
|
|
338
|
-
.option('-r, --ruleset <set>', 'rule set: default | strict | lenient', 'default')
|
|
339
|
-
.option('-v, --verbose', 'detailed report')
|
|
340
|
-
.option('-x, --fix', 'auto-fix trailing whitespace')
|
|
341
|
-
.action((file, options) => {
|
|
342
|
-
validateCommand(file, {
|
|
343
|
-
ruleset: options.ruleset,
|
|
344
|
-
verbose: options.verbose,
|
|
345
|
-
fix: options.fix
|
|
346
|
-
});
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
// ── memory ────────────────────────────────────────────────────
|
|
350
|
-
// Sub-commands to avoid the ambiguous "memory <action> --save key" pattern
|
|
351
|
-
const memCmd = program.command('memory').alias('mem').description('Manage team knowledge and memory');
|
|
352
|
-
|
|
353
|
-
memCmd
|
|
354
|
-
.command('save <key> <value>')
|
|
355
|
-
.alias('s')
|
|
356
|
-
.description('Save a memory entry')
|
|
357
|
-
.action((key, value) => { memoryCommand('save', { key, value }); });
|
|
358
|
-
|
|
359
|
-
memCmd
|
|
360
|
-
.command('get <key>')
|
|
361
|
-
.alias('g')
|
|
362
|
-
.description('Retrieve a memory entry')
|
|
363
|
-
.action((key) => { memoryCommand('get', { key }); });
|
|
364
|
-
|
|
365
|
-
memCmd
|
|
366
|
-
.command('list')
|
|
367
|
-
.alias('ls')
|
|
368
|
-
.description('List all memories')
|
|
369
|
-
.action(() => { memoryCommand('list'); });
|
|
370
|
-
|
|
371
|
-
memCmd
|
|
372
|
-
.command('search <query>')
|
|
373
|
-
.alias('sr')
|
|
374
|
-
.description('Search memories by keyword')
|
|
375
|
-
.action((query) => { memoryCommand('search', { query }); });
|
|
376
|
-
|
|
377
|
-
memCmd
|
|
378
|
-
.command('delete <key>')
|
|
379
|
-
.alias('d')
|
|
380
|
-
.description('Delete a memory entry')
|
|
381
|
-
.action((key) => { memoryCommand('delete', { key }); });
|
|
382
|
-
|
|
383
|
-
memCmd
|
|
384
|
-
.command('clear')
|
|
385
|
-
.alias('cl')
|
|
386
|
-
.description('Clear all memories')
|
|
387
|
-
.action(() => { memoryCommand('clear'); });
|
|
388
|
-
|
|
389
|
-
// ── guide ─────────────────────────────────────────────────────
|
|
390
|
-
program
|
|
391
|
-
.command('guide')
|
|
392
|
-
.alias('g')
|
|
393
|
-
.description('Show quickstart guide, architecture, and command reference')
|
|
394
|
-
.option('-f, --flow', 'show architecture & flow diagram only')
|
|
395
|
-
.option('-c, --commands', 'show command reference only')
|
|
396
|
-
.action((options) => {
|
|
397
|
-
guideCommand(options);
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
// ── telemetry feedback helper ─────────────────────────────────
|
|
401
|
-
function showTelResult(result, label) {
|
|
402
|
-
try {
|
|
403
|
-
if (!result) return;
|
|
404
|
-
if (result.ok) {
|
|
405
|
-
console.log(chalk.green(` [tel] ✓ ${label} logged`));
|
|
406
|
-
} else if (result.reason === 'disabled') {
|
|
407
|
-
console.log(chalk.yellow(` [tel] ⚠ skipped — telemetry is disabled (run 'aiflow telemetry enable')`));
|
|
408
|
-
} else if (result.reason === 'opted-out') {
|
|
409
|
-
console.log(chalk.yellow(` [tel] ⚠ skipped — repo has .aiflow/no-telemetry opt-out`));
|
|
410
|
-
} else if (result.reason === 'no-url') {
|
|
411
|
-
console.log(chalk.yellow(` [tel] ⚠ skipped — no server URL configured (run 'aiflow telemetry enable')`));
|
|
412
|
-
} else if (result.error) {
|
|
413
|
-
console.log(chalk.yellow(` [tel] ⚠ error logging '${label}': ${result.error}`));
|
|
414
|
-
}
|
|
415
|
-
} catch (e) { /* never block main flow */ }
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// ── gate (telemetry for gate workflow events) ─────────────────
|
|
419
|
-
program
|
|
420
|
-
.command('gate <number> <action>')
|
|
421
|
-
.description('Log a gate workflow event (start|approved) — called by AI during gate workflow')
|
|
422
|
-
.option('--ticket <id>', 'ticket ID')
|
|
423
|
-
.option('--ai-tool <tool>', 'AI tool name')
|
|
424
|
-
.action((number, action, options) => {
|
|
425
|
-
const gateNum = parseInt(number);
|
|
426
|
-
if (isNaN(gateNum) || gateNum < 1 || gateNum > 5) return;
|
|
427
|
-
if (!['start', 'approved'].includes(action)) return;
|
|
428
|
-
|
|
429
|
-
const ticketId = options.ticket || '';
|
|
430
|
-
|
|
431
|
-
const telResult = record(`gate.${action}`, {
|
|
432
|
-
gate: gateNum,
|
|
433
|
-
ticket_id: ticketId,
|
|
434
|
-
command: `gate${gateNum}.${action}`,
|
|
435
|
-
ai_tool: options.aiTool || ''
|
|
436
|
-
});
|
|
437
|
-
showTelResult(telResult, `gate${gateNum}.${action}`);
|
|
438
|
-
|
|
439
|
-
// Keep task-state.json currentGate in sync (silent — never blocks the AI)
|
|
440
|
-
if (ticketId) {
|
|
441
|
-
updateTaskGateState(ticketId, gateNum, action).catch(() => {});
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
if (action === 'approved') {
|
|
445
|
-
console.log(chalk.cyan(`\n Gate ${gateNum} approved!`));
|
|
446
|
-
console.log(chalk.yellow(` Pro-Tip: To avoid context pollution, it's recommended to start a fresh chat session.`));
|
|
447
|
-
console.log(chalk.gray(` Run: aiflow task next${ticketId ? ` --ticket ${ticketId}` : ''} to save progress,`));
|
|
448
|
-
console.log(chalk.gray(` then open a NEW chatbox and type "continue".\n`));
|
|
449
|
-
}
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
// ── remove ────────────────────────────────────────────────────
|
|
453
|
-
program
|
|
454
|
-
.command('remove')
|
|
455
|
-
.alias('rm')
|
|
456
|
-
.description('Remove ai-flow-kit from project, a cached version, or uninstall globally')
|
|
457
|
-
.option('--version <ver>', 'remove a specific cached version from .aiflow/versions/')
|
|
458
|
-
.option('-g, --global', 'uninstall the global npm package (removes `aiflow` command)')
|
|
459
|
-
.action((options) => {
|
|
460
|
-
removeCommand(options);
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
// ── update ────────────────────────────────────────────────────
|
|
464
|
-
program
|
|
465
|
-
.command('update')
|
|
466
|
-
.alias('up')
|
|
467
|
-
.description('Update to latest version')
|
|
468
|
-
.option('-f, --force', 'force update even if already on latest')
|
|
469
|
-
.action((options) => {
|
|
470
|
-
updateCommand(options);
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
// ── sync-skills ───────────────────────────────────────────────
|
|
474
|
-
program
|
|
475
|
-
.command('sync-skills')
|
|
476
|
-
.alias('sync')
|
|
477
|
-
.description('Manually synchronize AI Instruction files with local custom skills')
|
|
478
|
-
.action(async () => {
|
|
479
|
-
const projectDir = process.cwd();
|
|
480
|
-
const { setupFramework, AI_TOOL_FILES, ensureAiflowGitignored } = require('../scripts/init');
|
|
481
|
-
const fs = require('fs-extra');
|
|
482
|
-
const path = require('path');
|
|
483
|
-
const chalk = require('chalk');
|
|
484
|
-
|
|
485
|
-
const stateFile = path.join(projectDir, '.aiflow', 'state.json');
|
|
486
|
-
if (!(await fs.pathExists(stateFile))) {
|
|
487
|
-
console.log(chalk.red('Project is not initialized. Please run `aiflow init` first.'));
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
const state = await fs.readJson(stateFile);
|
|
492
|
-
const frameworks = state.frameworks || [];
|
|
493
|
-
const selectedTools = state.aiTools || Object.keys(AI_TOOL_FILES);
|
|
494
|
-
|
|
495
|
-
console.log(chalk.cyan('⟳ Syncing AI Instruction files...'));
|
|
496
|
-
for (const fw of frameworks) {
|
|
497
|
-
await setupFramework(projectDir, fw, frameworks.length > 1, selectedTools);
|
|
498
|
-
}
|
|
499
|
-
await ensureAiflowGitignored(projectDir);
|
|
500
|
-
console.log(chalk.green('✨ Sync completed!'));
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
// ── doctor ────────────────────────────────────────────────────
|
|
504
|
-
program
|
|
505
|
-
.command('doctor')
|
|
506
|
-
.alias('dr')
|
|
507
|
-
.description('Health check of AI Flow Kit setup')
|
|
508
|
-
.option('-t, --ticket <id>', 'show token breakdown for specific ticket')
|
|
509
|
-
.option('-v, --verbose', 'detailed output')
|
|
510
|
-
.action((opts) => {
|
|
511
|
-
doctorCommand({ ticket: opts.ticket, verbose: opts.verbose });
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
// ── telemetry ──────────────────────────────────────────────────
|
|
515
|
-
program
|
|
516
|
-
.command('telemetry [action]')
|
|
517
|
-
.alias('tel')
|
|
518
|
-
.description('Manage telemetry logging (enable|disable|status|log)')
|
|
519
|
-
.option('--event <event>', 'event name (for log action)')
|
|
520
|
-
.option('--detail <detail>', 'detail string (for log action)')
|
|
521
|
-
.action((action, options) => {
|
|
522
|
-
telemetryCommand(action || 'status', options);
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
program.parse(process.argv);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { program } = require('commander');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const packageJson = require('../package.json');
|
|
6
|
+
const initCommand = require('../scripts/init');
|
|
7
|
+
const updateCommand = require('../scripts/update');
|
|
8
|
+
const doctorCommand = require('../scripts/doctor');
|
|
9
|
+
const useCommand = require('../scripts/use');
|
|
10
|
+
const TaskDetector = require('../scripts/detect');
|
|
11
|
+
const validateCommand = require('../scripts/validate');
|
|
12
|
+
const memoryCommand = require('../scripts/memory');
|
|
13
|
+
const contextCommand = require('../scripts/context');
|
|
14
|
+
const promptCommand = require('../scripts/prompt');
|
|
15
|
+
const removeCommand = require('../scripts/remove');
|
|
16
|
+
const guideCommand = require('../scripts/guide');
|
|
17
|
+
const telemetryCommand= require('../scripts/telemetry/cli');
|
|
18
|
+
const taskCommand = require('../scripts/task');
|
|
19
|
+
const checkpointCommand = require('../scripts/checkpoint');
|
|
20
|
+
const { record } = require('../scripts/telemetry/record');
|
|
21
|
+
const { updateTaskGateState } = require('../scripts/task');
|
|
22
|
+
|
|
23
|
+
// ── DEPRECATION WARNING ───────────────────────────────────────
|
|
24
|
+
if (!process.env.AIFLOW_IS_AK) {
|
|
25
|
+
console.warn(chalk.yellow('\n⚠ Warning: "aiflow" command is deprecated and will be removed in future versions.'));
|
|
26
|
+
console.warn(chalk.yellow('Please use the shorter "ak" command instead.\n'));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
program
|
|
30
|
+
.version(packageJson.version)
|
|
31
|
+
.description('AI Flow Kit CLI - AI-powered development workflows for teams');
|
|
32
|
+
|
|
33
|
+
// ── GLOBAL TELEMETRY HOOK ────────────────────────────────────
|
|
34
|
+
// We intercept raw argv here so we can catch --version, --help,
|
|
35
|
+
// and invalid commands *before* Commander exits the process.
|
|
36
|
+
// Also alias `-v` to commander's `-V` at the root only — subcommands still
|
|
37
|
+
// keep `-v` as `--verbose`. And alias `--fw` to `--framework`.
|
|
38
|
+
(() => {
|
|
39
|
+
if (process.argv[2] === '-v') process.argv[2] = '-V';
|
|
40
|
+
for (let i = 3; i < process.argv.length; i++) {
|
|
41
|
+
if (process.argv[i] === '--fw') process.argv[i] = '--framework';
|
|
42
|
+
}
|
|
43
|
+
const args = process.argv.slice(2);
|
|
44
|
+
let cmd = 'help';
|
|
45
|
+
if (args.length > 0) {
|
|
46
|
+
let first = args[0];
|
|
47
|
+
|
|
48
|
+
// 1. Phân giải alias cho root commands
|
|
49
|
+
const aliases = {
|
|
50
|
+
'i': 'init',
|
|
51
|
+
'u': 'use',
|
|
52
|
+
'p': 'prompt',
|
|
53
|
+
'd': 'detect',
|
|
54
|
+
't': 'task',
|
|
55
|
+
'ctx': 'context',
|
|
56
|
+
'cp': 'checkpoint',
|
|
57
|
+
'vl': 'validate',
|
|
58
|
+
'mem': 'memory',
|
|
59
|
+
'g': 'guide',
|
|
60
|
+
'rm': 'remove',
|
|
61
|
+
'up': 'update',
|
|
62
|
+
'sync': 'sync-skills',
|
|
63
|
+
'dr': 'doctor',
|
|
64
|
+
'tel': 'telemetry'
|
|
65
|
+
};
|
|
66
|
+
if (aliases[first]) {
|
|
67
|
+
first = aliases[first];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (['-V', '-v', '--version'].includes(first)) {
|
|
71
|
+
cmd = 'version';
|
|
72
|
+
} else if (['-h', '--help'].includes(first)) {
|
|
73
|
+
cmd = 'help';
|
|
74
|
+
} else if (first.startsWith('-')) {
|
|
75
|
+
cmd = 'unknown_flag';
|
|
76
|
+
if (args.includes('--help') || args.includes('-h')) cmd = 'help';
|
|
77
|
+
} else if (first === 'memory') {
|
|
78
|
+
const sec = args[1];
|
|
79
|
+
// Phân giải alias cho memory subcommands
|
|
80
|
+
let sub = sec;
|
|
81
|
+
if (sec && !sec.startsWith('-')) {
|
|
82
|
+
const memAliases = {
|
|
83
|
+
's': 'save',
|
|
84
|
+
'g': 'get',
|
|
85
|
+
'ls': 'list',
|
|
86
|
+
'sr': 'search',
|
|
87
|
+
'd': 'delete',
|
|
88
|
+
'cl': 'clear'
|
|
89
|
+
};
|
|
90
|
+
if (memAliases[sec]) sub = memAliases[sec];
|
|
91
|
+
cmd = `memory.${sub}`;
|
|
92
|
+
} else {
|
|
93
|
+
cmd = 'memory';
|
|
94
|
+
}
|
|
95
|
+
} else if (first === 'task') {
|
|
96
|
+
const sec = args[1];
|
|
97
|
+
// Phân giải alias subcommand của task
|
|
98
|
+
let sub = sec;
|
|
99
|
+
if (sec && !sec.startsWith('-')) {
|
|
100
|
+
const subAliases = {
|
|
101
|
+
'st': 'status',
|
|
102
|
+
'ls': 'list',
|
|
103
|
+
'p': 'pause',
|
|
104
|
+
'sw': 'switch',
|
|
105
|
+
'r': 'resume',
|
|
106
|
+
'rst': 'reset',
|
|
107
|
+
'rm': 'remove',
|
|
108
|
+
'n': 'next'
|
|
109
|
+
};
|
|
110
|
+
if (subAliases[sec]) sub = subAliases[sec];
|
|
111
|
+
cmd = `task.${sub}`;
|
|
112
|
+
} else {
|
|
113
|
+
cmd = 'task';
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
cmd = first;
|
|
117
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
118
|
+
cmd = `${first}.help`;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Record everything except telemetry and gate actions (gate records its own rich event)
|
|
123
|
+
if (cmd !== 'telemetry' && !cmd.startsWith('telemetry.') && cmd !== 'gate') {
|
|
124
|
+
record('command.invoked', { command: cmd });
|
|
125
|
+
}
|
|
126
|
+
})();
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
// ── init ──────────────────────────────────────────────────────
|
|
130
|
+
program
|
|
131
|
+
.command('init')
|
|
132
|
+
.alias('i')
|
|
133
|
+
.description('Initialize AI Flow Kit in your project')
|
|
134
|
+
.option('-f, --framework <types>', 'framework(s), comma-separated (e.g. spring-boot,reactjs)')
|
|
135
|
+
.option('-a, --adapter <types>', 'adapter(s), comma-separated (e.g. backlog,jira)')
|
|
136
|
+
.option('-e, --env <types>', 'AI environment(s)/tool(s), comma-separated (e.g. cursor,gemini,copilot)')
|
|
137
|
+
.option('--with-rtk', 'force enable RTK token compression hook')
|
|
138
|
+
.option('--no-rtk', 'skip RTK setup even if RTK is detected')
|
|
139
|
+
.action((options) => {
|
|
140
|
+
options.frameworks = options.framework
|
|
141
|
+
? options.framework.split(',').map(s => s.trim()).filter(Boolean)
|
|
142
|
+
: [];
|
|
143
|
+
options.adapters = options.adapter
|
|
144
|
+
? options.adapter.split(',').map(s => s.trim()).filter(Boolean)
|
|
145
|
+
: [];
|
|
146
|
+
options.aiTools = options.env
|
|
147
|
+
? options.env.split(',').map(s => s.trim()).filter(Boolean)
|
|
148
|
+
: Object.keys(require('../scripts/init').AI_TOOL_FILES || {}).filter(t => t !== 'generic');
|
|
149
|
+
|
|
150
|
+
initCommand(options);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// ── use ───────────────────────────────────────────────────────
|
|
154
|
+
program
|
|
155
|
+
.command('use [targets...]')
|
|
156
|
+
.alias('u')
|
|
157
|
+
.description('Load context from ticket(s), version, or file(s). First target is primary; rest become supplementary context.')
|
|
158
|
+
.option('-m, --manual', 'manual context entry')
|
|
159
|
+
.option('-f, --file <path>', 'load from file')
|
|
160
|
+
.option('-s, --save <name>', 'save as named context')
|
|
161
|
+
.option('-c, --coms', 'load all comments (alias for --with-comments)')
|
|
162
|
+
.option('--with-comments', 'load all comments')
|
|
163
|
+
.option('--cid <id>', 'load a specific comment by ID')
|
|
164
|
+
.option('--clast <n>', 'load the last N comments', parseInt)
|
|
165
|
+
.option('--cfrom <id>', 'load comments from ID N onward', parseInt)
|
|
166
|
+
.option('--cto <id>', 'load comments up to ID N', parseInt)
|
|
167
|
+
.option('-F, --fast', 'fast mode: minimal Q&A, concise requirement doc (Default)')
|
|
168
|
+
.option('-U, --full', 'full mode: force complete analysis with Q&A (default)')
|
|
169
|
+
.addHelpText('after', `
|
|
170
|
+
Examples:
|
|
171
|
+
$ ak use PROJ-33 Single ticket (primary only)
|
|
172
|
+
$ ak use PROJ-33 PROJ-10 docs/arch.md Multi-target: PROJ-33 primary, rest supplementary (v0.1.0+)
|
|
173
|
+
$ ak use https://company.backlog.com/view/PROJ-33 Load by Backlog URL
|
|
174
|
+
$ ak use PROJ-33 -c Include all comments
|
|
175
|
+
$ ak use PROJ-33 --clast 5 Newest 5 comments only
|
|
176
|
+
$ ak use --file task.md Load from local file
|
|
177
|
+
$ ak use --manual Manual entry
|
|
178
|
+
|
|
179
|
+
Auto link resolution (v0.1.0+):
|
|
180
|
+
Backlog/Jira URLs in the primary ticket description are auto-fetched and added
|
|
181
|
+
to supplementaryContext[]. Comment links (#comment-456 or ?focusedCommentId=456)
|
|
182
|
+
fetch only that comment. Capped at 5 auto-resolved links per call.
|
|
183
|
+
|
|
184
|
+
Related:
|
|
185
|
+
ak fetch-links <url> Fetch a single Backlog/Jira link → JSON (AI runtime use)
|
|
186
|
+
`)
|
|
187
|
+
.action((targets, options) => {
|
|
188
|
+
useCommand(targets, options);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// ── fetch-links ───────────────────────────────────────────────
|
|
192
|
+
program
|
|
193
|
+
.command('fetch-links <url>')
|
|
194
|
+
.description('Fetch a backlog/jira link and output SupplementaryContext JSON to stdout. Used by AI at runtime.')
|
|
195
|
+
.addHelpText('after', `
|
|
196
|
+
Examples:
|
|
197
|
+
$ ak fetch-links "https://company.backlog.com/view/PROJ-10"
|
|
198
|
+
$ ak fetch-links "https://company.backlog.com/view/PROJ-10#comment-456"
|
|
199
|
+
$ ak fetch-links "https://company.atlassian.net/browse/PROJ-10"
|
|
200
|
+
$ ak fetch-links "https://company.atlassian.net/browse/PROJ-10?focusedCommentId=789"
|
|
201
|
+
|
|
202
|
+
Exit codes:
|
|
203
|
+
0 Success — JSON written to stdout
|
|
204
|
+
1 URL not recognised, or fetch failed (error on stderr)
|
|
205
|
+
`)
|
|
206
|
+
.action(async (url) => {
|
|
207
|
+
const { fetchLink } = require('../scripts/link-resolver');
|
|
208
|
+
const { loadCredentials } = require('../scripts/use');
|
|
209
|
+
const credentials = await loadCredentials();
|
|
210
|
+
try {
|
|
211
|
+
const result = await fetchLink(url, credentials);
|
|
212
|
+
if (!result) {
|
|
213
|
+
process.stderr.write(`Not a recognized backlog/jira URL: ${url}\n`);
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
217
|
+
} catch (err) {
|
|
218
|
+
process.stderr.write(`Error fetching link: ${err.message}\n`);
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// ── prompt ────────────────────────────────────────────────────
|
|
224
|
+
program
|
|
225
|
+
.command('prompt [type]')
|
|
226
|
+
.alias('p')
|
|
227
|
+
.description('Generate AI prompt with context and rules')
|
|
228
|
+
.option('-l, --list', 'list available prompt types')
|
|
229
|
+
.option('-o, --output <file>', 'save to file')
|
|
230
|
+
.option('-L, --lang <lang>', 'language: english (default) or vietnamese')
|
|
231
|
+
.option('-d, --detail <level>', 'detail level: minimal | standard (default) | comprehensive')
|
|
232
|
+
.action((type, options) => {
|
|
233
|
+
promptCommand(type, options);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// ── detect ────────────────────────────────────────────────────
|
|
237
|
+
program
|
|
238
|
+
.command('detect [description]')
|
|
239
|
+
.alias('d')
|
|
240
|
+
.description('Auto-detect task type from description')
|
|
241
|
+
.option('-v, --verbose', 'show detection details')
|
|
242
|
+
.option('-t, --threshold <number>', 'confidence threshold (0-100)')
|
|
243
|
+
.action((description, options) => {
|
|
244
|
+
const detector = new TaskDetector();
|
|
245
|
+
const threshold = options.threshold ? parseInt(options.threshold) / 100 : 0.8;
|
|
246
|
+
const result = detector.detect(description, threshold);
|
|
247
|
+
console.log(chalk.cyan('\nTask Detection Results:\n'));
|
|
248
|
+
console.log(detector.formatOutput(result, options.verbose));
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// ── task ─────────────────────────────────────────────────────
|
|
252
|
+
const taskCmd = program.command('task').alias('t').description('Manage multiple tasks — pause, switch, resume');
|
|
253
|
+
|
|
254
|
+
taskCmd
|
|
255
|
+
.command('status')
|
|
256
|
+
.alias('st')
|
|
257
|
+
.description('Show active task and pending tasks')
|
|
258
|
+
.action(() => { taskCommand('status'); });
|
|
259
|
+
|
|
260
|
+
taskCmd
|
|
261
|
+
.command('list')
|
|
262
|
+
.alias('ls')
|
|
263
|
+
.description('List all saved tasks')
|
|
264
|
+
.action(() => { taskCommand('list'); });
|
|
265
|
+
|
|
266
|
+
taskCmd
|
|
267
|
+
.command('pause')
|
|
268
|
+
.alias('p')
|
|
269
|
+
.description('Pause current task and save progress')
|
|
270
|
+
.option('-n, --note <text>', 'note about why you are pausing')
|
|
271
|
+
.action((opts) => { taskCommand('pause', { note: opts.note }); });
|
|
272
|
+
|
|
273
|
+
taskCmd
|
|
274
|
+
.command('switch <ticket-id>')
|
|
275
|
+
.alias('sw')
|
|
276
|
+
.description('Pause current task and switch to another')
|
|
277
|
+
.action((ticketId) => { taskCommand('switch', { taskId: ticketId }); });
|
|
278
|
+
|
|
279
|
+
taskCmd
|
|
280
|
+
.command('resume <ticket-id>')
|
|
281
|
+
.alias('r')
|
|
282
|
+
.description('Resume a paused task')
|
|
283
|
+
.action((ticketId) => { taskCommand('resume', { taskId: ticketId }); });
|
|
284
|
+
|
|
285
|
+
taskCmd
|
|
286
|
+
.command('reset <ticket-id>')
|
|
287
|
+
.alias('rst')
|
|
288
|
+
.description('Reset task to Gate 1 (keeps ticket context, clears gate progress)')
|
|
289
|
+
.action((ticketId) => { taskCommand('reset', { taskId: ticketId }); });
|
|
290
|
+
|
|
291
|
+
taskCmd
|
|
292
|
+
.command('remove <ticket-id>')
|
|
293
|
+
.alias('rm')
|
|
294
|
+
.description('Permanently delete all saved data for a task')
|
|
295
|
+
.action((ticketId) => { taskCommand('remove', { taskId: ticketId }); });
|
|
296
|
+
|
|
297
|
+
taskCmd
|
|
298
|
+
.command('next')
|
|
299
|
+
.alias('n')
|
|
300
|
+
.description('Approve current gate and prepare next session (saves state for resume)')
|
|
301
|
+
.option('-t, --ticket <id>', 'ticket ID (defaults to active task)')
|
|
302
|
+
.action((opts) => {
|
|
303
|
+
taskCommand('next', { taskId: opts.ticket });
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// ── context ───────────────────────────────────────────────────
|
|
307
|
+
program
|
|
308
|
+
.command('context [action]')
|
|
309
|
+
.alias('ctx')
|
|
310
|
+
.description('Manage task contexts (list|show|save <name>|clear)')
|
|
311
|
+
.option('-s, --save <name>', 'save current context with name')
|
|
312
|
+
.option('-l, --load <name>', 'load saved context by name')
|
|
313
|
+
.option('-d, --delete <name>', 'delete saved context')
|
|
314
|
+
.option('--clear', 'clear all saved contexts')
|
|
315
|
+
.option('--show', 'show current context')
|
|
316
|
+
.action((action, options) => {
|
|
317
|
+
contextCommand(action, options);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// ── checkpoint ────────────────────────────────────────────────
|
|
321
|
+
program
|
|
322
|
+
.command('checkpoint')
|
|
323
|
+
.alias('cp')
|
|
324
|
+
.description('Record token usage checkpoint (called by AI during gate work)')
|
|
325
|
+
.requiredOption('-g, --gate <n>', 'gate number (1–5)', parseInt)
|
|
326
|
+
.requiredOption('-s, --step <name>', 'step name (e.g. "tests-written")')
|
|
327
|
+
.requiredOption('-n, --tokens <n>', 'estimated token count', parseInt)
|
|
328
|
+
.option('-t, --ticket <id>', 'ticket ID (defaults to active task)')
|
|
329
|
+
.action((opts) => {
|
|
330
|
+
checkpointCommand({ gate: opts.gate, step: opts.step, tokens: opts.tokens, ticket: opts.ticket });
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// ── validate ──────────────────────────────────────────────────
|
|
334
|
+
program
|
|
335
|
+
.command('validate <file>')
|
|
336
|
+
.alias('vl')
|
|
337
|
+
.description('Validate output against team rules')
|
|
338
|
+
.option('-r, --ruleset <set>', 'rule set: default | strict | lenient', 'default')
|
|
339
|
+
.option('-v, --verbose', 'detailed report')
|
|
340
|
+
.option('-x, --fix', 'auto-fix trailing whitespace')
|
|
341
|
+
.action((file, options) => {
|
|
342
|
+
validateCommand(file, {
|
|
343
|
+
ruleset: options.ruleset,
|
|
344
|
+
verbose: options.verbose,
|
|
345
|
+
fix: options.fix
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// ── memory ────────────────────────────────────────────────────
|
|
350
|
+
// Sub-commands to avoid the ambiguous "memory <action> --save key" pattern
|
|
351
|
+
const memCmd = program.command('memory').alias('mem').description('Manage team knowledge and memory');
|
|
352
|
+
|
|
353
|
+
memCmd
|
|
354
|
+
.command('save <key> <value>')
|
|
355
|
+
.alias('s')
|
|
356
|
+
.description('Save a memory entry')
|
|
357
|
+
.action((key, value) => { memoryCommand('save', { key, value }); });
|
|
358
|
+
|
|
359
|
+
memCmd
|
|
360
|
+
.command('get <key>')
|
|
361
|
+
.alias('g')
|
|
362
|
+
.description('Retrieve a memory entry')
|
|
363
|
+
.action((key) => { memoryCommand('get', { key }); });
|
|
364
|
+
|
|
365
|
+
memCmd
|
|
366
|
+
.command('list')
|
|
367
|
+
.alias('ls')
|
|
368
|
+
.description('List all memories')
|
|
369
|
+
.action(() => { memoryCommand('list'); });
|
|
370
|
+
|
|
371
|
+
memCmd
|
|
372
|
+
.command('search <query>')
|
|
373
|
+
.alias('sr')
|
|
374
|
+
.description('Search memories by keyword')
|
|
375
|
+
.action((query) => { memoryCommand('search', { query }); });
|
|
376
|
+
|
|
377
|
+
memCmd
|
|
378
|
+
.command('delete <key>')
|
|
379
|
+
.alias('d')
|
|
380
|
+
.description('Delete a memory entry')
|
|
381
|
+
.action((key) => { memoryCommand('delete', { key }); });
|
|
382
|
+
|
|
383
|
+
memCmd
|
|
384
|
+
.command('clear')
|
|
385
|
+
.alias('cl')
|
|
386
|
+
.description('Clear all memories')
|
|
387
|
+
.action(() => { memoryCommand('clear'); });
|
|
388
|
+
|
|
389
|
+
// ── guide ─────────────────────────────────────────────────────
|
|
390
|
+
program
|
|
391
|
+
.command('guide')
|
|
392
|
+
.alias('g')
|
|
393
|
+
.description('Show quickstart guide, architecture, and command reference')
|
|
394
|
+
.option('-f, --flow', 'show architecture & flow diagram only')
|
|
395
|
+
.option('-c, --commands', 'show command reference only')
|
|
396
|
+
.action((options) => {
|
|
397
|
+
guideCommand(options);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// ── telemetry feedback helper ─────────────────────────────────
|
|
401
|
+
function showTelResult(result, label) {
|
|
402
|
+
try {
|
|
403
|
+
if (!result) return;
|
|
404
|
+
if (result.ok) {
|
|
405
|
+
console.log(chalk.green(` [tel] ✓ ${label} logged`));
|
|
406
|
+
} else if (result.reason === 'disabled') {
|
|
407
|
+
console.log(chalk.yellow(` [tel] ⚠ skipped — telemetry is disabled (run 'aiflow telemetry enable')`));
|
|
408
|
+
} else if (result.reason === 'opted-out') {
|
|
409
|
+
console.log(chalk.yellow(` [tel] ⚠ skipped — repo has .aiflow/no-telemetry opt-out`));
|
|
410
|
+
} else if (result.reason === 'no-url') {
|
|
411
|
+
console.log(chalk.yellow(` [tel] ⚠ skipped — no server URL configured (run 'aiflow telemetry enable')`));
|
|
412
|
+
} else if (result.error) {
|
|
413
|
+
console.log(chalk.yellow(` [tel] ⚠ error logging '${label}': ${result.error}`));
|
|
414
|
+
}
|
|
415
|
+
} catch (e) { /* never block main flow */ }
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// ── gate (telemetry for gate workflow events) ─────────────────
|
|
419
|
+
program
|
|
420
|
+
.command('gate <number> <action>')
|
|
421
|
+
.description('Log a gate workflow event (start|approved) — called by AI during gate workflow')
|
|
422
|
+
.option('--ticket <id>', 'ticket ID')
|
|
423
|
+
.option('--ai-tool <tool>', 'AI tool name')
|
|
424
|
+
.action((number, action, options) => {
|
|
425
|
+
const gateNum = parseInt(number);
|
|
426
|
+
if (isNaN(gateNum) || gateNum < 1 || gateNum > 5) return;
|
|
427
|
+
if (!['start', 'approved'].includes(action)) return;
|
|
428
|
+
|
|
429
|
+
const ticketId = options.ticket || '';
|
|
430
|
+
|
|
431
|
+
const telResult = record(`gate.${action}`, {
|
|
432
|
+
gate: gateNum,
|
|
433
|
+
ticket_id: ticketId,
|
|
434
|
+
command: `gate${gateNum}.${action}`,
|
|
435
|
+
ai_tool: options.aiTool || ''
|
|
436
|
+
});
|
|
437
|
+
showTelResult(telResult, `gate${gateNum}.${action}`);
|
|
438
|
+
|
|
439
|
+
// Keep task-state.json currentGate in sync (silent — never blocks the AI)
|
|
440
|
+
if (ticketId) {
|
|
441
|
+
updateTaskGateState(ticketId, gateNum, action).catch(() => {});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (action === 'approved') {
|
|
445
|
+
console.log(chalk.cyan(`\n Gate ${gateNum} approved!`));
|
|
446
|
+
console.log(chalk.yellow(` Pro-Tip: To avoid context pollution, it's recommended to start a fresh chat session.`));
|
|
447
|
+
console.log(chalk.gray(` Run: aiflow task next${ticketId ? ` --ticket ${ticketId}` : ''} to save progress,`));
|
|
448
|
+
console.log(chalk.gray(` then open a NEW chatbox and type "continue".\n`));
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
// ── remove ────────────────────────────────────────────────────
|
|
453
|
+
program
|
|
454
|
+
.command('remove')
|
|
455
|
+
.alias('rm')
|
|
456
|
+
.description('Remove ai-flow-kit from project, a cached version, or uninstall globally')
|
|
457
|
+
.option('--version <ver>', 'remove a specific cached version from .aiflow/versions/')
|
|
458
|
+
.option('-g, --global', 'uninstall the global npm package (removes `aiflow` command)')
|
|
459
|
+
.action((options) => {
|
|
460
|
+
removeCommand(options);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// ── update ────────────────────────────────────────────────────
|
|
464
|
+
program
|
|
465
|
+
.command('update')
|
|
466
|
+
.alias('up')
|
|
467
|
+
.description('Update to latest version')
|
|
468
|
+
.option('-f, --force', 'force update even if already on latest')
|
|
469
|
+
.action((options) => {
|
|
470
|
+
updateCommand(options);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// ── sync-skills ───────────────────────────────────────────────
|
|
474
|
+
program
|
|
475
|
+
.command('sync-skills')
|
|
476
|
+
.alias('sync')
|
|
477
|
+
.description('Manually synchronize AI Instruction files with local custom skills')
|
|
478
|
+
.action(async () => {
|
|
479
|
+
const projectDir = process.cwd();
|
|
480
|
+
const { setupFramework, AI_TOOL_FILES, ensureAiflowGitignored } = require('../scripts/init');
|
|
481
|
+
const fs = require('fs-extra');
|
|
482
|
+
const path = require('path');
|
|
483
|
+
const chalk = require('chalk');
|
|
484
|
+
|
|
485
|
+
const stateFile = path.join(projectDir, '.aiflow', 'state.json');
|
|
486
|
+
if (!(await fs.pathExists(stateFile))) {
|
|
487
|
+
console.log(chalk.red('Project is not initialized. Please run `aiflow init` first.'));
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const state = await fs.readJson(stateFile);
|
|
492
|
+
const frameworks = state.frameworks || [];
|
|
493
|
+
const selectedTools = state.aiTools || Object.keys(AI_TOOL_FILES);
|
|
494
|
+
|
|
495
|
+
console.log(chalk.cyan('⟳ Syncing AI Instruction files...'));
|
|
496
|
+
for (const fw of frameworks) {
|
|
497
|
+
await setupFramework(projectDir, fw, frameworks.length > 1, selectedTools);
|
|
498
|
+
}
|
|
499
|
+
await ensureAiflowGitignored(projectDir);
|
|
500
|
+
console.log(chalk.green('✨ Sync completed!'));
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
// ── doctor ────────────────────────────────────────────────────
|
|
504
|
+
program
|
|
505
|
+
.command('doctor')
|
|
506
|
+
.alias('dr')
|
|
507
|
+
.description('Health check of AI Flow Kit setup')
|
|
508
|
+
.option('-t, --ticket <id>', 'show token breakdown for specific ticket')
|
|
509
|
+
.option('-v, --verbose', 'detailed output')
|
|
510
|
+
.action((opts) => {
|
|
511
|
+
doctorCommand({ ticket: opts.ticket, verbose: opts.verbose });
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
// ── telemetry ──────────────────────────────────────────────────
|
|
515
|
+
program
|
|
516
|
+
.command('telemetry [action]')
|
|
517
|
+
.alias('tel')
|
|
518
|
+
.description('Manage telemetry logging (enable|disable|status|log)')
|
|
519
|
+
.option('--event <event>', 'event name (for log action)')
|
|
520
|
+
.option('--detail <detail>', 'detail string (for log action)')
|
|
521
|
+
.action((action, options) => {
|
|
522
|
+
telemetryCommand(action || 'status', options);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
program.parse(process.argv);
|