@colin4k1024/tsp 2.5.2 → 2.5.3

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.
Files changed (36) hide show
  1. package/bin/lib/install-surface.js +5 -0
  2. package/hooks/harness-statusline.js +34 -11
  3. package/manifests/install-modules.json +98 -31
  4. package/package.json +2 -1
  5. package/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  6. package/scripts/__pycache__/build_platform_artifacts.cpython-311.pyc +0 -0
  7. package/scripts/__pycache__/install_platform.cpython-311.pyc +0 -0
  8. package/scripts/__pycache__/langfuse_trace.cpython-311.pyc +0 -0
  9. package/scripts/__pycache__/query_audit_logs.cpython-311.pyc +0 -0
  10. package/scripts/__pycache__/scan_leaked_keys.cpython-311.pyc +0 -0
  11. package/scripts/__pycache__/team_skills_platform.cpython-311.pyc +0 -0
  12. package/scripts/__pycache__/team_skills_platform.cpython-313.pyc +0 -0
  13. package/scripts/__pycache__/validate_library.cpython-311.pyc +0 -0
  14. package/scripts/__pycache__/validate_workflow_state.cpython-311.pyc +0 -0
  15. package/scripts/evolution/__pycache__/__init__.cpython-311.pyc +0 -0
  16. package/scripts/evolution/__pycache__/store.cpython-311.pyc +0 -0
  17. package/scripts/hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  18. package/scripts/hooks/__pycache__/mcp_health_check.cpython-311.pyc +0 -0
  19. package/scripts/hooks/__pycache__/observe.cpython-311.pyc +0 -0
  20. package/scripts/hooks/__pycache__/session_end.cpython-311.pyc +0 -0
  21. package/scripts/hooks/__pycache__/session_start.cpython-311.pyc +0 -0
  22. package/scripts/hooks/suggest-compact.js +41 -0
  23. package/scripts/lib/__pycache__/audit_logger.cpython-311.pyc +0 -0
  24. package/scripts/lib/__pycache__/audit_query.cpython-311.pyc +0 -0
  25. package/scripts/lib/__pycache__/hook_contract.cpython-311.pyc +0 -0
  26. package/scripts/lib/__pycache__/memory_store.cpython-311.pyc +0 -0
  27. package/scripts/lib/__pycache__/utils.cpython-311.pyc +0 -0
  28. package/scripts/lib/install/request.js +1 -1
  29. package/scripts/lib/install-manifests.js +9 -1
  30. package/scripts/lib/install-targets/cangming-home.js +143 -0
  31. package/scripts/lib/install-targets/codewhale-home.js +187 -0
  32. package/scripts/lib/install-targets/registry.js +5 -1
  33. package/scripts/lib/transcript-usage.js +183 -0
  34. package/scripts/test-cangming-install.js +105 -0
  35. package/skills/goframe-v2/examples/practices/quick-demo/manifest/config/config.yaml +14 -14
  36. package/skills/repo-scan/SKILL.md +63 -63
@@ -0,0 +1,143 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const {
5
+ createInstallTargetAdapter,
6
+ createManagedOperation,
7
+ normalizeRelativePath,
8
+ } = require('./helpers');
9
+
10
+ const PLUGIN_NAME = require('../team-skills-data.json').plugin.name;
11
+
12
+ function addOperation(operations, seen, operation) {
13
+ const key = `${operation.sourceRelativePath}=>${operation.destinationPath}`;
14
+ if (seen.has(key)) {
15
+ return;
16
+ }
17
+ seen.add(key);
18
+ operations.push(operation);
19
+ }
20
+
21
+ function addCopyOperation(operations, seen, moduleId, sourceRelativePath, destinationPath, strategy = 'preserve-relative-path') {
22
+ addOperation(operations, seen, createManagedOperation({
23
+ moduleId,
24
+ sourceRelativePath,
25
+ destinationPath,
26
+ strategy,
27
+ }));
28
+ }
29
+
30
+ function sourceDir(input, sourceRelativePath) {
31
+ return path.join(input.repoRoot || '', normalizeRelativePath(sourceRelativePath));
32
+ }
33
+
34
+ function rootAgentFileName(sourcePath, fileName) {
35
+ const normalizedSourcePath = normalizeRelativePath(sourcePath);
36
+ if (normalizedSourcePath === 'agents/specialists' || normalizedSourcePath.startsWith('agents/specialists/')) {
37
+ return `specialist-${fileName}`;
38
+ }
39
+ return fileName;
40
+ }
41
+
42
+ function addRootAgentOperations(operations, seen, moduleId, sourceRelativePath, input, targetRoot) {
43
+ const normalizedSourcePath = normalizeRelativePath(sourceRelativePath);
44
+ if (normalizedSourcePath !== 'agents' && !normalizedSourcePath.startsWith('agents/')) {
45
+ return;
46
+ }
47
+
48
+ const agentDirs = normalizedSourcePath === 'agents'
49
+ ? ['roles', 'specialists']
50
+ : [''];
51
+
52
+ for (const agentDir of agentDirs) {
53
+ const sourcePath = agentDir ? path.join(normalizedSourcePath, agentDir) : normalizedSourcePath;
54
+ const absoluteSourceDir = sourceDir(input, sourcePath);
55
+ if (!input.repoRoot || !fs.existsSync(absoluteSourceDir)) {
56
+ continue;
57
+ }
58
+
59
+ const stat = fs.statSync(absoluteSourceDir);
60
+ if (stat.isFile() && path.extname(absoluteSourceDir) === '.md') {
61
+ const fileName = rootAgentFileName(path.dirname(sourcePath), path.basename(sourcePath));
62
+ addCopyOperation(
63
+ operations,
64
+ seen,
65
+ moduleId,
66
+ sourcePath,
67
+ path.join(targetRoot, 'agents', fileName),
68
+ 'flatten-agent-copy'
69
+ );
70
+ continue;
71
+ }
72
+
73
+ if (!stat.isDirectory()) {
74
+ continue;
75
+ }
76
+
77
+ for (const entry of fs.readdirSync(absoluteSourceDir, { withFileTypes: true }).sort((left, right) => (
78
+ left.name.localeCompare(right.name)
79
+ ))) {
80
+ if (entry.isFile() && entry.name.endsWith('.md')) {
81
+ const fileName = rootAgentFileName(sourcePath, entry.name);
82
+ addCopyOperation(
83
+ operations,
84
+ seen,
85
+ moduleId,
86
+ path.join(sourcePath, entry.name),
87
+ path.join(targetRoot, 'agents', fileName),
88
+ 'flatten-agent-copy'
89
+ );
90
+ }
91
+ }
92
+ }
93
+ }
94
+
95
+ function planCangmingOperations(input, adapter) {
96
+ const targetRoot = adapter.resolveRoot(input);
97
+ const pluginRoot = path.join(targetRoot, 'plugins', PLUGIN_NAME);
98
+ const operations = [];
99
+ const seen = new Set();
100
+
101
+ for (const module of Array.isArray(input.modules) ? input.modules : []) {
102
+ for (const rawSourcePath of Array.isArray(module.paths) ? module.paths : []) {
103
+ const sourceRelativePath = normalizeRelativePath(rawSourcePath);
104
+
105
+ addCopyOperation(
106
+ operations,
107
+ seen,
108
+ module.id,
109
+ sourceRelativePath,
110
+ path.join(pluginRoot, sourceRelativePath),
111
+ 'plugin-copy'
112
+ );
113
+
114
+ if (sourceRelativePath === 'commands' || sourceRelativePath.startsWith('commands/')) {
115
+ const commandSuffix = sourceRelativePath === 'commands'
116
+ ? ''
117
+ : sourceRelativePath.slice('commands/'.length);
118
+ addCopyOperation(
119
+ operations,
120
+ seen,
121
+ module.id,
122
+ sourceRelativePath,
123
+ path.join(targetRoot, 'command', commandSuffix),
124
+ 'opencode-command-copy'
125
+ );
126
+ }
127
+
128
+ addRootAgentOperations(operations, seen, module.id, sourceRelativePath, input, targetRoot);
129
+ }
130
+ }
131
+
132
+ return operations;
133
+ }
134
+
135
+ module.exports = createInstallTargetAdapter({
136
+ id: 'cangming-home',
137
+ target: 'cangming',
138
+ kind: 'home',
139
+ rootSegments: ['.config', 'cangming'],
140
+ installStatePathSegments: ['ecc-install-state.json'],
141
+ nativeRootRelativePath: '.cangming',
142
+ planOperations: planCangmingOperations,
143
+ });
@@ -0,0 +1,187 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const {
5
+ createInstallTargetAdapter,
6
+ createManagedOperation,
7
+ normalizeRelativePath,
8
+ } = require('./helpers');
9
+
10
+ const PLUGIN_NAME = require('../team-skills-data.json').plugin.name;
11
+
12
+ function addOperation(operations, seen, operation) {
13
+ const key = `${operation.sourceRelativePath}=>${operation.destinationPath}`;
14
+ if (seen.has(key)) {
15
+ return;
16
+ }
17
+ seen.add(key);
18
+ operations.push(operation);
19
+ }
20
+
21
+ function addCopyOperation(operations, seen, moduleId, sourceRelativePath, destinationPath, strategy = 'preserve-relative-path') {
22
+ addOperation(operations, seen, createManagedOperation({
23
+ moduleId,
24
+ sourceRelativePath,
25
+ destinationPath,
26
+ strategy,
27
+ }));
28
+ }
29
+
30
+ function sourceDir(input, sourceRelativePath) {
31
+ return path.join(input.repoRoot || '', normalizeRelativePath(sourceRelativePath));
32
+ }
33
+
34
+ function addAgentOperations(operations, seen, moduleId, sourceRelativePath, input, targetRoot) {
35
+ const normalizedSourcePath = normalizeRelativePath(sourceRelativePath);
36
+ if (normalizedSourcePath !== 'agents' && !normalizedSourcePath.startsWith('agents/')) {
37
+ return;
38
+ }
39
+
40
+ const agentDirs = normalizedSourcePath === 'agents'
41
+ ? ['roles', 'specialists']
42
+ : [''];
43
+
44
+ for (const agentDir of agentDirs) {
45
+ const sourcePath = agentDir ? path.join(normalizedSourcePath, agentDir) : normalizedSourcePath;
46
+ const absoluteSourceDir = sourceDir(input, sourcePath);
47
+ if (!input.repoRoot || !fs.existsSync(absoluteSourceDir)) {
48
+ continue;
49
+ }
50
+
51
+ const stat = fs.statSync(absoluteSourceDir);
52
+ if (stat.isFile() && path.extname(absoluteSourceDir) === '.md') {
53
+ const fileName = path.basename(sourcePath);
54
+ addCopyOperation(
55
+ operations, seen, moduleId, sourcePath,
56
+ path.join(targetRoot, 'agents', fileName),
57
+ 'flatten-agent-copy'
58
+ );
59
+ continue;
60
+ }
61
+
62
+ if (!stat.isDirectory()) {
63
+ continue;
64
+ }
65
+
66
+ for (const entry of fs.readdirSync(absoluteSourceDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
67
+ if (entry.isFile() && entry.name.endsWith('.md')) {
68
+ const prefix = agentDir === 'specialists' ? 'specialist-' : '';
69
+ addCopyOperation(
70
+ operations, seen, moduleId,
71
+ path.join(sourcePath, entry.name),
72
+ path.join(targetRoot, 'agents', `${prefix}${entry.name}`),
73
+ 'flatten-agent-copy'
74
+ );
75
+ }
76
+ }
77
+ }
78
+ }
79
+
80
+ function addRuleOperations(operations, seen, moduleId, sourceRelativePath, input, targetRoot) {
81
+ const normalizedSourcePath = normalizeRelativePath(sourceRelativePath);
82
+ if (normalizedSourcePath !== 'rules' && !normalizedSourcePath.startsWith('rules/')) {
83
+ return;
84
+ }
85
+
86
+ const absoluteSourceDir = sourceDir(input, normalizedSourcePath);
87
+ if (!input.repoRoot || !fs.existsSync(absoluteSourceDir) || !fs.statSync(absoluteSourceDir).isDirectory()) {
88
+ return;
89
+ }
90
+
91
+ const entries = fs.readdirSync(absoluteSourceDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
92
+ for (const entry of entries) {
93
+ const entryPath = path.join(absoluteSourceDir, entry.name);
94
+ if (entry.isDirectory()) {
95
+ const subEntries = fs.readdirSync(entryPath, { withFileTypes: true });
96
+ for (const sub of subEntries) {
97
+ if (sub.isFile() && sub.name.endsWith('.md')) {
98
+ addCopyOperation(
99
+ operations, seen, moduleId,
100
+ path.join(normalizedSourcePath, entry.name, sub.name),
101
+ path.join(targetRoot, 'rules', entry.name, sub.name),
102
+ 'rule-copy'
103
+ );
104
+ }
105
+ }
106
+ } else if (entry.isFile() && entry.name.endsWith('.md')) {
107
+ addCopyOperation(
108
+ operations, seen, moduleId,
109
+ path.join(normalizedSourcePath, entry.name),
110
+ path.join(targetRoot, 'rules', entry.name),
111
+ 'rule-copy'
112
+ );
113
+ }
114
+ }
115
+ }
116
+
117
+ function planCodeWhaleOperations(input, adapter) {
118
+ const targetRoot = adapter.resolveRoot(input);
119
+ const operations = [];
120
+ const seen = new Set();
121
+
122
+ for (const module of Array.isArray(input.modules) ? input.modules : []) {
123
+ for (const rawSourcePath of Array.isArray(module.paths) ? module.paths : []) {
124
+ const sourceRelativePath = normalizeRelativePath(rawSourcePath);
125
+
126
+ // Skills: preserve directory structure
127
+ if (sourceRelativePath === 'skills' || sourceRelativePath.startsWith('skills/')) {
128
+ addCopyOperation(
129
+ operations, seen, module.id, sourceRelativePath,
130
+ path.join(targetRoot, 'skills', sourceRelativePath.replace(/^skills\/?/, '')),
131
+ 'skill-copy'
132
+ );
133
+ }
134
+
135
+ // Commands: flatten to commands/
136
+ if (sourceRelativePath === 'commands' || sourceRelativePath.startsWith('commands/')) {
137
+ const commandSuffix = sourceRelativePath === 'commands'
138
+ ? ''
139
+ : sourceRelativePath.slice('commands/'.length);
140
+ addCopyOperation(
141
+ operations, seen, module.id, sourceRelativePath,
142
+ path.join(targetRoot, 'commands', commandSuffix),
143
+ 'command-copy'
144
+ );
145
+ }
146
+
147
+ // Agents: flatten roles + specialists
148
+ addAgentOperations(operations, seen, module.id, sourceRelativePath, input, targetRoot);
149
+
150
+ // Rules: preserve namespace/file structure
151
+ addRuleOperations(operations, seen, module.id, sourceRelativePath, input, targetRoot);
152
+
153
+ // Contexts: direct copy
154
+ if (sourceRelativePath === 'contexts' || sourceRelativePath.startsWith('contexts/')) {
155
+ const contextSuffix = sourceRelativePath === 'contexts'
156
+ ? ''
157
+ : sourceRelativePath.slice('contexts/'.length);
158
+ addCopyOperation(
159
+ operations, seen, module.id, sourceRelativePath,
160
+ path.join(targetRoot, 'contexts', contextSuffix),
161
+ 'context-copy'
162
+ );
163
+ }
164
+
165
+ // Hooks: copy hooks directory for reference, config.toml injection handled by post-install
166
+ if (sourceRelativePath === 'hooks' || sourceRelativePath.startsWith('hooks/')) {
167
+ addCopyOperation(
168
+ operations, seen, module.id, sourceRelativePath,
169
+ path.join(targetRoot, 'hooks', sourceRelativePath.replace(/^hooks\/?/, '')),
170
+ 'hook-copy'
171
+ );
172
+ }
173
+ }
174
+ }
175
+
176
+ return operations;
177
+ }
178
+
179
+ module.exports = createInstallTargetAdapter({
180
+ id: 'codewhale-home',
181
+ target: 'codewhale',
182
+ kind: 'home',
183
+ rootSegments: ['.codewhale'],
184
+ installStatePathSegments: ['ecc-install-state.json'],
185
+ nativeRootRelativePath: '.codewhale',
186
+ planOperations: planCodeWhaleOperations,
187
+ });
@@ -1,15 +1,17 @@
1
1
  const antigravityProject = require('./antigravity-project');
2
2
  const augmentProject = require('./augment-project');
3
+ const cangmingHome = require('./cangming-home');
3
4
  const claudeHome = require('./claude-home');
4
5
  const codebuddyProject = require('./codebuddy-project');
5
6
  const codexHome = require('./codex-home');
7
+ const codewhaleHome = require('./codewhale-home');
6
8
  const copilotHome = require('./copilot-home');
7
9
  const cursorProject = require('./cursor-project');
8
10
  const geminiProject = require('./gemini-project');
9
11
  const opencodeHome = require('./opencode-home');
10
12
  const windsurfProject = require('./windsurf-project');
11
13
 
12
- const PUBLIC_INSTALL_TARGETS = Object.freeze(['claude', 'codex', 'opencode']);
14
+ const PUBLIC_INSTALL_TARGETS = Object.freeze(['claude', 'codex', 'opencode', 'cangming', 'codewhale']);
13
15
  const TARGET_ALIASES = Object.freeze({
14
16
  'claude-code': 'claude',
15
17
  claudecode: 'claude',
@@ -22,6 +24,8 @@ const ADAPTERS = Object.freeze([
22
24
  codexHome,
23
25
  geminiProject,
24
26
  opencodeHome,
27
+ cangmingHome,
28
+ codewhaleHome,
25
29
  codebuddyProject,
26
30
  copilotHome,
27
31
  windsurfProject,
@@ -0,0 +1,183 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const DEFAULT_CONTEXT_LIMIT = 200000;
7
+ const EXTENDED_CONTEXT_LIMIT = 1000000;
8
+ const DEFAULT_TAIL_BYTES = 65536;
9
+ const EXPANDED_TAIL_BYTES = 262144;
10
+
11
+ function resolveContextLimit(modelId) {
12
+ const envLimit = Number(process.env.CLAUDE_CONTEXT_LIMIT);
13
+ if (Number.isFinite(envLimit) && envLimit > 0) return envLimit;
14
+
15
+ if (typeof modelId === 'string' && /\[1[mM]\]/i.test(modelId)) {
16
+ return EXTENDED_CONTEXT_LIMIT;
17
+ }
18
+
19
+ return DEFAULT_CONTEXT_LIMIT;
20
+ }
21
+
22
+ function readTailLines(filePath, tailBytes = DEFAULT_TAIL_BYTES) {
23
+ let fd;
24
+ try {
25
+ const stat = fs.statSync(filePath);
26
+ if (stat.size === 0) return [];
27
+
28
+ const readSize = Math.min(tailBytes, stat.size);
29
+ const buffer = Buffer.alloc(readSize);
30
+ fd = fs.openSync(filePath, 'r');
31
+ fs.readSync(fd, buffer, 0, readSize, stat.size - readSize);
32
+ fs.closeSync(fd);
33
+ fd = null;
34
+
35
+ const text = buffer.toString('utf8');
36
+ const lines = text.split('\n');
37
+
38
+ // Drop the first line if we started mid-file (likely partial)
39
+ if (stat.size > readSize && lines.length > 0) {
40
+ lines.shift();
41
+ }
42
+
43
+ return lines.filter(line => line.trim().length > 0);
44
+ } catch (_) {
45
+ if (fd != null) try { fs.closeSync(fd); } catch (__) { /* ignore */ }
46
+ return [];
47
+ }
48
+ }
49
+
50
+ function normalizeUsage(raw) {
51
+ if (!raw || typeof raw !== 'object') return null;
52
+
53
+ const inputTokens = Number(raw.input_tokens || raw.prompt_tokens || 0) || 0;
54
+ const outputTokens = Number(raw.output_tokens || raw.completion_tokens || 0) || 0;
55
+ const cacheCreationTokens = Number(raw.cache_creation_input_tokens || raw.cache_creation_prompt_tokens || 0) || 0;
56
+ const cacheReadTokens = Number(raw.cache_read_input_tokens || raw.cache_read_prompt_tokens || raw.cached_tokens || 0) || 0;
57
+
58
+ const contextTokens = inputTokens + outputTokens + cacheCreationTokens + cacheReadTokens;
59
+ if (contextTokens === 0) return null;
60
+
61
+ return {
62
+ inputTokens,
63
+ outputTokens,
64
+ cacheCreationTokens,
65
+ cacheReadTokens,
66
+ contextTokens,
67
+ };
68
+ }
69
+
70
+ function parseTranscriptUsage(transcriptPath) {
71
+ if (!transcriptPath || typeof transcriptPath !== 'string') return null;
72
+ if (!fs.existsSync(transcriptPath)) return null;
73
+
74
+ let lines = readTailLines(transcriptPath, DEFAULT_TAIL_BYTES);
75
+
76
+ // Scan from bottom up for the last assistant message with usage
77
+ for (let i = lines.length - 1; i >= 0; i--) {
78
+ const line = lines[i].trim();
79
+ if (!line) continue;
80
+
81
+ let entry;
82
+ try {
83
+ entry = JSON.parse(line);
84
+ } catch (_) {
85
+ continue;
86
+ }
87
+
88
+ // Handle summary entries — they indicate compaction happened
89
+ if (entry.type === 'summary' && entry.leafUuid) {
90
+ const projectDir = path.dirname(transcriptPath);
91
+ const usage = findUsageByLeafUuid(entry.leafUuid, projectDir);
92
+ if (usage) return usage;
93
+ continue;
94
+ }
95
+
96
+ if (entry.type !== 'assistant') continue;
97
+ if (!entry.message || !entry.message.usage) continue;
98
+
99
+ return normalizeUsage(entry.message.usage);
100
+ }
101
+
102
+ // If not found in default tail, try expanded read
103
+ if (lines.length > 0) {
104
+ lines = readTailLines(transcriptPath, EXPANDED_TAIL_BYTES);
105
+ for (let i = lines.length - 1; i >= 0; i--) {
106
+ const line = lines[i].trim();
107
+ if (!line) continue;
108
+
109
+ let entry;
110
+ try {
111
+ entry = JSON.parse(line);
112
+ } catch (_) {
113
+ continue;
114
+ }
115
+
116
+ if (entry.type !== 'assistant') continue;
117
+ if (!entry.message || !entry.message.usage) continue;
118
+
119
+ return normalizeUsage(entry.message.usage);
120
+ }
121
+ }
122
+
123
+ return null;
124
+ }
125
+
126
+ function findUsageByLeafUuid(leafUuid, projectDir) {
127
+ if (!projectDir || !fs.existsSync(projectDir)) return null;
128
+
129
+ let sessionFiles;
130
+ try {
131
+ sessionFiles = fs.readdirSync(projectDir)
132
+ .filter(f => f.endsWith('.jsonl'))
133
+ .map(f => path.join(projectDir, f));
134
+ } catch (_) {
135
+ return null;
136
+ }
137
+
138
+ for (const filePath of sessionFiles) {
139
+ const lines = readTailLines(filePath, EXPANDED_TAIL_BYTES);
140
+ for (let i = lines.length - 1; i >= 0; i--) {
141
+ const line = lines[i].trim();
142
+ if (!line) continue;
143
+
144
+ let entry;
145
+ try {
146
+ entry = JSON.parse(line);
147
+ } catch (_) {
148
+ continue;
149
+ }
150
+
151
+ if (entry.uuid === leafUuid && entry.type === 'assistant' && entry.message?.usage) {
152
+ return normalizeUsage(entry.message.usage);
153
+ }
154
+ }
155
+ }
156
+
157
+ return null;
158
+ }
159
+
160
+ function resolveTranscriptMetrics(transcriptPath, modelId) {
161
+ const usage = parseTranscriptUsage(transcriptPath);
162
+ if (!usage) return null;
163
+
164
+ const contextLimit = resolveContextLimit(modelId);
165
+ const usagePct = Math.max(0, Math.min(100, Math.round((usage.contextTokens / contextLimit) * 100)));
166
+
167
+ return {
168
+ usagePct,
169
+ contextTokens: usage.contextTokens,
170
+ contextLimit,
171
+ source: 'transcript_usage',
172
+ };
173
+ }
174
+
175
+ module.exports = {
176
+ resolveContextLimit,
177
+ readTailLines,
178
+ parseTranscriptUsage,
179
+ resolveTranscriptMetrics,
180
+ normalizeUsage,
181
+ DEFAULT_CONTEXT_LIMIT,
182
+ EXTENDED_CONTEXT_LIMIT,
183
+ };
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+
6
+ const CANGMING_HOME = path.join(os.homedir(), '.config', 'cangming');
7
+ const PLUGIN_DIR = path.join(CANGMING_HOME, 'plugins', 'team-skills-platform');
8
+
9
+ console.log('=== Cangming 安装验证 ===\n');
10
+
11
+ let passed = 0;
12
+ let failed = 0;
13
+
14
+ function check(description, condition) {
15
+ if (condition) {
16
+ console.log(`✅ ${description}`);
17
+ passed++;
18
+ } else {
19
+ console.log(`❌ ${description}`);
20
+ failed++;
21
+ }
22
+ }
23
+
24
+ console.log('📁 目录结构检查:');
25
+ check('CANGMING_HOME 目录存在', fs.existsSync(CANGMING_HOME));
26
+ check('AGENTS.md 文件存在', fs.existsSync(path.join(CANGMING_HOME, 'AGENTS.md')));
27
+ check('agents 目录存在', fs.existsSync(path.join(CANGMING_HOME, 'agents')));
28
+ check('command 目录存在', fs.existsSync(path.join(CANGMING_HOME, 'command')));
29
+ check('plugins 目录存在', fs.existsSync(path.join(CANGMING_HOME, 'plugins')));
30
+ check('team-skills-platform 插件存在', fs.existsSync(PLUGIN_DIR));
31
+
32
+ console.log('\n📄 文件内容检查:');
33
+
34
+ const agentsMdPath = path.join(CANGMING_HOME, 'AGENTS.md');
35
+ if (fs.existsSync(agentsMdPath)) {
36
+ const content = fs.readFileSync(agentsMdPath, 'utf8');
37
+ check('AGENTS.md 包含团队技能平台标记', content.includes('<!-- team-skills-platform -->'));
38
+ check('AGENTS.md 包含角色索引', content.includes('## 可用角色'));
39
+ check('AGENTS.md 包含命令索引', content.includes('## 核心团队命令'));
40
+ check('AGENTS.md 包含插件根路径', content.includes('## 插件根路径'));
41
+ }
42
+
43
+ console.log('\n👥 Agents 检查:');
44
+ const agentsDir = path.join(CANGMING_HOME, 'agents');
45
+ if (fs.existsSync(agentsDir)) {
46
+ const agentFiles = fs.readdirSync(agentsDir).filter(f => f.endsWith('.md'));
47
+ check(`agents 目录包含文件 (${agentFiles.length})`, agentFiles.length > 0);
48
+
49
+ const roleAgents = ['tech-lead.md', 'product-manager.md', 'architect.md', 'frontend-engineer.md',
50
+ 'backend-engineer.md', 'qa-engineer.md', 'devops-engineer.md'];
51
+ for (const agent of roleAgents) {
52
+ check(`角色 agent ${agent} 存在`, fs.existsSync(path.join(agentsDir, agent)));
53
+ }
54
+
55
+ const specialistAgents = agentFiles.filter(f => f.startsWith('specialist-'));
56
+ check(`specialist agents 存在 (${specialistAgents.length})`, specialistAgents.length > 0);
57
+ }
58
+
59
+ console.log('\n📝 Commands 检查:');
60
+ const commandsDir = path.join(CANGMING_HOME, 'command');
61
+ if (fs.existsSync(commandsDir)) {
62
+ const commandFiles = fs.readdirSync(commandsDir).filter(f => f.endsWith('.md'));
63
+ check(`commands 目录包含文件 (${commandFiles.length})`, commandFiles.length > 0);
64
+
65
+ const coreCommands = ['team-intake.md', 'team-plan.md', 'team-execute.md', 'team-review.md',
66
+ 'team-release.md', 'handoff.md'];
67
+ for (const cmd of coreCommands) {
68
+ check(`核心命令 ${cmd} 存在`, fs.existsSync(path.join(commandsDir, cmd)));
69
+ }
70
+ }
71
+
72
+ console.log('\n🎯 Skills 检查:');
73
+ const skillsDir = path.join(PLUGIN_DIR, 'skills');
74
+ if (fs.existsSync(skillsDir)) {
75
+ const skillDirs = fs.readdirSync(skillsDir, { withFileTypes: true })
76
+ .filter(d => d.isDirectory())
77
+ .map(d => d.name);
78
+ check(`skills 目录包含目录 (${skillDirs.length})`, skillDirs.length > 0);
79
+ }
80
+
81
+ console.log('\n📜 Rules 检查:');
82
+ const rulesDir = path.join(PLUGIN_DIR, 'rules');
83
+ if (fs.existsSync(rulesDir)) {
84
+ const ruleItems = fs.readdirSync(rulesDir);
85
+ check(`rules 目录包含内容 (${ruleItems.length})`, ruleItems.length > 0);
86
+ check('common 规则目录存在', fs.existsSync(path.join(rulesDir, 'common')));
87
+ }
88
+
89
+ // 总结
90
+ console.log('\n=== 测试总结 ===');
91
+ console.log(`通过: ${passed}`);
92
+ console.log(`失败: ${failed}`);
93
+ console.log(`总计: ${passed + failed}`);
94
+
95
+ if (failed === 0) {
96
+ console.log('\n🎉 所有测试通过!Cangming 安装成功。');
97
+ console.log('\n下一步:');
98
+ console.log('1. 启动 Cangming: cangming');
99
+ console.log('2. 查看可用角色: AGENTS.md 中包含所有角色索引');
100
+ console.log('3. 执行团队命令: /team-intake, /team-plan 等');
101
+ process.exit(0);
102
+ } else {
103
+ console.log('\n⚠️ 部分测试失败,请检查安装。');
104
+ process.exit(1);
105
+ }
@@ -1,15 +1,15 @@
1
- # https://goframe.org/docs/web/server-config-file-template
2
- server:
3
- address: ":8000"
4
- openapiPath: "/api.json"
5
- swaggerPath: "/swagger"
6
-
7
- # https://goframe.org/docs/core/glog-config
8
- logger:
9
- level : "all"
10
- stdout: true
11
-
12
- # https://goframe.org/docs/core/gdb-config-file
13
- database:
14
- default:
1
+ # https://goframe.org/docs/web/server-config-file-template
2
+ server:
3
+ address: ":8000"
4
+ openapiPath: "/api.json"
5
+ swaggerPath: "/swagger"
6
+
7
+ # https://goframe.org/docs/core/glog-config
8
+ logger:
9
+ level : "all"
10
+ stdout: true
11
+
12
+ # https://goframe.org/docs/core/gdb-config-file
13
+ database:
14
+ default:
15
15
  link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"