thachvd-kit 1.0.33 → 1.0.34

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/bin/entry.js CHANGED
@@ -4,6 +4,8 @@ const {
4
4
  snapshotPolicyFiles,
5
5
  applyPolicyUpgrades
6
6
  } = require('./policy');
7
+ const { upgradeProject } = require('./upgrade');
8
+ const { installGlobalPolicies } = require('./global');
7
9
 
8
10
  function isInitInvocation(args) {
9
11
  if (args.includes('--help') || args.includes('-h') || args.includes('--version') || args.includes('-v')) {
@@ -13,7 +15,63 @@ function isInitInvocation(args) {
13
15
  return !first || first === 'init' || first.startsWith('-');
14
16
  }
15
17
 
18
+ function runUpgrade(args) {
19
+ const results = upgradeProject(process.cwd(), { dryRun: args.includes('--dry-run') });
20
+ const changed = results.filter(result => result.changed);
21
+ if (changed.length === 0) {
22
+ console.log('No managed project policy changes needed.');
23
+ return;
24
+ }
25
+ for (const result of changed) {
26
+ console.log(`${result.dryRun ? 'WOULD UPDATE' : 'UPDATED'} ${result.path}`);
27
+ }
28
+ }
29
+
30
+ function runGlobal(args) {
31
+ const homeDir = process.env.USERPROFILE || process.env.HOME;
32
+ if (!homeDir) {
33
+ console.error('Cannot determine home directory from USERPROFILE or HOME.');
34
+ process.exitCode = 1;
35
+ return;
36
+ }
37
+
38
+ const antigravityOnly = args.includes('--antigravity-only');
39
+ const codexOnly = args.includes('--codex-only');
40
+ if (antigravityOnly && codexOnly) {
41
+ console.error('Choose at most one of --antigravity-only or --codex-only.');
42
+ process.exitCode = 1;
43
+ return;
44
+ }
45
+
46
+ const results = installGlobalPolicies(homeDir, {
47
+ antigravityOnly,
48
+ codexOnly,
49
+ dryRun: args.includes('--dry-run')
50
+ });
51
+
52
+ for (const result of results) {
53
+ const status = result.changed ? (result.dryRun ? 'WOULD UPDATE' : 'UPDATED') : 'OK';
54
+ console.log(`${status} ${result.path}`);
55
+ }
56
+
57
+ if (results.some(result => result.changed) && !args.includes('--dry-run')) {
58
+ console.log('Restart Antigravity/Codex sessions so the updated global instructions are reloaded.');
59
+ }
60
+ }
61
+
16
62
  const args = process.argv.slice(2);
63
+ const command = args[0];
64
+
65
+ if (command === 'upgrade') {
66
+ runUpgrade(args.slice(1));
67
+ return;
68
+ }
69
+
70
+ if (command === 'global') {
71
+ runGlobal(args.slice(1));
72
+ return;
73
+ }
74
+
17
75
  const shouldUpgradeGeneratedPolicy = isInitInvocation(args);
18
76
  const beforeSnapshot = shouldUpgradeGeneratedPolicy ? snapshotPolicyFiles(process.cwd()) : null;
19
77
  const force = args.includes('--yes') || args.includes('-y');
package/bin/global.js ADDED
@@ -0,0 +1,103 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const START = '<!-- thachvd-kit:global-tool-routing:start -->';
5
+ const END = '<!-- thachvd-kit:global-tool-routing:end -->';
6
+
7
+ const GLOBAL_TOOL_ROUTING = `${START}
8
+ ## AI Coding Tool Routing
9
+
10
+ Optimize tool usage for correctness and context efficiency.
11
+
12
+ ### Core principle
13
+
14
+ Use the cheapest reliable source that can answer the question. Do not perform broad repository searches when structural tools can answer the question more precisely, and do not retrieve the same evidence twice once sufficient context is available.
15
+
16
+ This routing policy supersedes older blanket instructions such as “ALWAYS prefer MCP graph tools over grep/glob/file-search”. Graph-first is preferred for structural discovery, not for every search.
17
+
18
+ ### Code discovery
19
+
20
+ - For unknown code ownership, symbols, callers/callees, architecture, dependencies, or impact analysis, prefer one available structural index such as \`codebase-memory-mcp\` or CodeGraph.
21
+ - If multiple structural indexes are available, choose one first. Do not query both for the same question unless the first result is insufficient or stale.
22
+ - Once the relevant file or symbol is known, read only the specific code needed for the task.
23
+
24
+ ### Direct search
25
+
26
+ Use \`rg\`, \`grep\`, globbing, or direct file reads when they are the more precise and cheaper operation, including exact strings, error messages, config values, environment variables, TODO/FIXME markers, filenames, non-code files, and known narrow locations.
27
+
28
+ Avoid broad repository-wide text searches for structural questions.
29
+
30
+ ### Shell output
31
+
32
+ Prefer RTK for verbose commands when it supports the command and preserves the information needed, especially git status/diff/log, tests, builds, package-manager commands, Docker, Kubernetes, and noisy linters.
33
+
34
+ Use raw command output when RTK is unavailable, incompatible, or raw output is specifically required for diagnosis.
35
+
36
+ ### Context budget
37
+
38
+ Treat tool output as part of the model context budget. Narrow searches and commands before running them, avoid duplicate retrieval, and request only the relevant file, symbol, range, test, or log section.
39
+
40
+ Correctness takes priority over token savings. Never omit context required to make a safe and correct change.
41
+ ${END}`;
42
+
43
+ function normalize(text) {
44
+ return String(text || '').replace(/\r\n/g, '\n');
45
+ }
46
+
47
+ function upsertManagedBlock(content, block = GLOBAL_TOOL_ROUTING) {
48
+ const text = normalize(content);
49
+ const start = text.indexOf(START);
50
+ const end = text.indexOf(END);
51
+
52
+ if (start >= 0 && end >= start) {
53
+ const after = end + END.length;
54
+ return `${text.slice(0, start)}${block}${text.slice(after)}`.replace(/\n{3,}/g, '\n\n').trimEnd() + '\n';
55
+ }
56
+
57
+ const prefix = text.trimEnd();
58
+ return `${prefix}${prefix ? '\n\n' : ''}${block}\n`;
59
+ }
60
+
61
+ function resolveCodexGlobalTarget(homeDir) {
62
+ const codexDir = path.join(homeDir, '.codex');
63
+ const overridePath = path.join(codexDir, 'AGENTS.override.md');
64
+ if (fs.existsSync(overridePath) && fs.statSync(overridePath).size > 0) return overridePath;
65
+ return path.join(codexDir, 'AGENTS.md');
66
+ }
67
+
68
+ function installGlobalRouting(filePath, options = {}) {
69
+ const dryRun = options.dryRun === true;
70
+ const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '';
71
+ const updated = upsertManagedBlock(existing);
72
+ const changed = normalize(existing) !== updated;
73
+
74
+ if (changed && !dryRun) {
75
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
76
+ fs.writeFileSync(filePath, updated, 'utf8');
77
+ }
78
+
79
+ return { path: filePath, changed, dryRun: changed && dryRun };
80
+ }
81
+
82
+ function installGlobalPolicies(homeDir, options = {}) {
83
+ const antigravityOnly = options.antigravityOnly === true;
84
+ const codexOnly = options.codexOnly === true;
85
+ if (antigravityOnly && codexOnly) {
86
+ throw new Error('Choose at most one of antigravityOnly or codexOnly.');
87
+ }
88
+
89
+ const targets = [];
90
+ if (!codexOnly) targets.push(path.join(homeDir, '.gemini', 'GEMINI.md'));
91
+ if (!antigravityOnly) targets.push(resolveCodexGlobalTarget(homeDir));
92
+ return targets.map(filePath => installGlobalRouting(filePath, options));
93
+ }
94
+
95
+ module.exports = {
96
+ START,
97
+ END,
98
+ GLOBAL_TOOL_ROUTING,
99
+ upsertManagedBlock,
100
+ resolveCodexGlobalTarget,
101
+ installGlobalRouting,
102
+ installGlobalPolicies
103
+ };
package/bin/upgrade.js ADDED
@@ -0,0 +1,86 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const START = '<!-- thachvd-kit:project-policy:start -->';
5
+ const END = '<!-- thachvd-kit:project-policy:end -->';
6
+
7
+ const PROJECT_POLICY = `${START}
8
+ ## Engineering Policy
9
+
10
+ Prefer the smallest sufficient change, not the fewest lines of code.
11
+
12
+ - Preserve required behavior, product and architectural contracts, UX/accessibility, security/data integrity, compatibility/performance, and maintainability before optimizing for simplicity.
13
+ - Reuse an existing project abstraction when it satisfies the required contract. Do not bypass it merely because a lower-level primitive is shorter.
14
+ - Avoid speculative abstractions and unrelated refactors.
15
+ - Do not split or reorganize code solely to satisfy a line-count target. Prefer cohesive modules and split only for a concrete cohesion, ownership, testability, or maintainability benefit.
16
+ - Fast-path eligibility is based on risk and contract surface, not file count. A code change plus its focused test can still be trivial; a one-file auth, payment, schema, concurrency, or public-contract change is not.
17
+ - Use the cheapest reliable context source. Read/search directly for known local code and exact text; use one structural index such as codebase-memory-mcp or CodeGraph for unknown ownership, call paths, architecture, or impact; avoid duplicate retrieval once sufficient evidence is available.
18
+ - Prefer RTK for verbose shell output when it preserves the information needed. Use raw output when required for diagnosis.
19
+ - Tests or equivalent verification are mandatory before claiming completion.
20
+ ${END}`;
21
+
22
+ const TARGETS = [
23
+ 'AGENTS.md',
24
+ path.join('.agent', 'docs', 'conventions.md'),
25
+ path.join('.agent', 'docs', 'workflow.md'),
26
+ path.join('.agent', 'docs', 'tooling.md')
27
+ ];
28
+
29
+ function normalize(text) {
30
+ return String(text || '').replace(/\r\n/g, '\n');
31
+ }
32
+
33
+ function removeKnownObsoleteDefaults(text) {
34
+ return normalize(text)
35
+ .replace(/^\s*- Maximum file length: 300 lines unless the existing project standard is stricter\.\s*\n?/gm, '')
36
+ .replace(/^\s*- Keep files under 300 lines unless the project already has a different standard[^\n]*\n?/gm, '')
37
+ .replace(/^\s*- \*\*MCP First\*\*:[^\n]*\n?/gm, '')
38
+ .replace(/^\s*- Use MCP first for code discovery and technical documentation;[^\n]*\n?/gm, '')
39
+ .replace(/Use it only for one-file, unambiguous changes with no behavior, API, schema, security, dependency, CI, workflow, or release contract change\.[^\n]*/g,
40
+ 'Fast-path eligibility is based on risk and contract surface, not file count. Use it only for localized, mechanically obvious, low-risk changes with focused verification.')
41
+ .replace(/Use a fast path only for one-file, unambiguous changes with no behavior or contract change\.[^\n]*/g,
42
+ 'Use a fast path only for localized, mechanically obvious, low-risk changes with focused verification; file count alone is not a gate.')
43
+ .replace(/\n{3,}/g, '\n\n');
44
+ }
45
+
46
+ function upsertManagedBlock(content) {
47
+ let text = removeKnownObsoleteDefaults(content);
48
+ const start = text.indexOf(START);
49
+ const end = text.indexOf(END);
50
+
51
+ if (start >= 0 && end >= start) {
52
+ const after = end + END.length;
53
+ return `${text.slice(0, start)}${PROJECT_POLICY}${text.slice(after)}`.replace(/\n{3,}/g, '\n\n').trimEnd() + '\n';
54
+ }
55
+
56
+ const trimmed = text.trimEnd();
57
+ return `${trimmed}${trimmed ? '\n\n' : ''}${PROJECT_POLICY}\n`;
58
+ }
59
+
60
+ function upgradeProject(rootDir, options = {}) {
61
+ const dryRun = options.dryRun === true;
62
+ const results = [];
63
+
64
+ for (const relativePath of TARGETS) {
65
+ const fullPath = path.join(rootDir, relativePath);
66
+ if (!fs.existsSync(fullPath)) continue;
67
+
68
+ const existing = fs.readFileSync(fullPath, 'utf8');
69
+ const updated = upsertManagedBlock(existing);
70
+ const changed = normalize(existing) !== updated;
71
+ if (changed && !dryRun) fs.writeFileSync(fullPath, updated, 'utf8');
72
+ results.push({ path: relativePath, changed, dryRun: changed && dryRun });
73
+ }
74
+
75
+ return results;
76
+ }
77
+
78
+ module.exports = {
79
+ START,
80
+ END,
81
+ PROJECT_POLICY,
82
+ TARGETS,
83
+ removeKnownObsoleteDefaults,
84
+ upsertManagedBlock,
85
+ upgradeProject
86
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thachvd-kit",
3
- "version": "1.0.33",
3
+ "version": "1.0.34",
4
4
  "description": "Cross-agent project rules bootstrap kit for Codex, Antigravity, and Claude Code",
5
5
  "bin": {
6
6
  "thachvd-kit": "./bin/entry.js"
@@ -15,8 +15,8 @@
15
15
  "node": ">=16.7"
16
16
  },
17
17
  "scripts": {
18
- "test": "node test/cli.test.js && node test/policy.test.js",
19
- "release:verify": "npm test && node --check bin/cli.js && node --check bin/entry.js && node --check bin/policy.js && npm pack --dry-run",
18
+ "test": "node test/cli.test.js && node test/policy.test.js && node test/upgrade.test.js && node test/global.test.js",
19
+ "release:verify": "npm test && node --check bin/cli.js && node --check bin/entry.js && node --check bin/policy.js && node --check bin/upgrade.js && node --check bin/global.js && npm pack --dry-run",
20
20
  "preversion": "npm run release:verify",
21
21
  "release:patch": "npm version patch",
22
22
  "release:dry-run": "npm pack --dry-run",