coder-config 0.45.13 → 0.46.0-beta.7
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/config-loader.js +6 -1
- package/lib/constants.js +5 -3
- package/lib/workstreams.js +138 -27
- package/package.json +1 -1
- package/scripts/release.sh +74 -39
- package/ui/dist/assets/index-B4hzEzCz.css +32 -0
- package/ui/dist/assets/{index-DFzCZrS_.js → index-DoeTG2Q1.js} +102 -102
- package/ui/dist/index.html +2 -2
- package/ui/routes/workstreams.js +35 -4
- package/ui/server.cjs +10 -1
- package/ui/dist/assets/index-ChBU02w_.css +0 -32
package/config-loader.js
CHANGED
|
@@ -28,7 +28,7 @@ const { init, show } = require('./lib/init');
|
|
|
28
28
|
const { memoryList, memoryInit, memoryAdd, memorySearch } = require('./lib/memory');
|
|
29
29
|
const { envList, envSet, envUnset } = require('./lib/env');
|
|
30
30
|
const { getProjectsRegistryPath, loadProjectsRegistry, saveProjectsRegistry, projectList, projectAdd, projectRemove } = require('./lib/projects');
|
|
31
|
-
const { getWorkstreamsPath, loadWorkstreams, saveWorkstreams, workstreamList, workstreamCreate, workstreamUpdate, workstreamDelete, workstreamUse, workstreamActive, workstreamAddProject, workstreamRemoveProject, workstreamInject, workstreamDetect, workstreamGet, getActiveWorkstream, countWorkstreamsForProject, workstreamInstallHook, workstreamInstallHookGemini, workstreamInstallHookCodex, workstreamDeactivate, workstreamCheckPath, getSettingsPath, loadSettings, saveSettings, workstreamAddTrigger, workstreamRemoveTrigger, workstreamSetAutoActivate, setGlobalAutoActivate, shouldAutoActivate, workstreamCheckFolder, workstreamInstallCdHook, workstreamUninstallCdHook, workstreamCdHookStatus, generateRulesFromRepos, generateRulesWithClaude } = require('./lib/workstreams');
|
|
31
|
+
const { getWorkstreamsPath, loadWorkstreams, saveWorkstreams, workstreamList, workstreamCreate, workstreamUpdate, workstreamDelete, workstreamUse, workstreamActive, workstreamAddProject, workstreamRemoveProject, workstreamInject, workstreamDetect, workstreamGet, getActiveWorkstream, countWorkstreamsForProject, workstreamInstallHook, workstreamInstallHookGemini, workstreamInstallHookCodex, workstreamDeactivate, workstreamCheckPath, getSettingsPath, loadSettings, saveSettings, workstreamAddTrigger, workstreamRemoveTrigger, workstreamSetAutoActivate, setGlobalAutoActivate, shouldAutoActivate, workstreamCheckFolder, workstreamInstallCdHook, workstreamUninstallCdHook, workstreamCdHookStatus, discoverSubProjects, generateRulesFromRepos, generateRulesWithClaude, generateRulesWithAI, getAvailableAITools, findAIBinary, AI_TOOLS } = require('./lib/workstreams');
|
|
32
32
|
const { getActivityPath, getDefaultActivity, loadActivity, saveActivity, detectProjectRoot, activityLog, activitySummary, generateWorkstreamName, activitySuggestWorkstreams, activityClear } = require('./lib/activity');
|
|
33
33
|
const { getLoopsPath, loadLoops, saveLoops, loadLoopState, saveLoopState, loadHistory, saveHistory, loopList, loopCreate, loopGet, loopUpdate, loopDelete, loopStart, loopPause, loopResume, loopCancel, loopApprove, loopComplete, loopFail, loopStatus, loopHistory, loopConfig, getActiveLoop, recordIteration, saveClarifications, savePlan, loadClarifications, loadPlan, loopInject, archiveLoop } = require('./lib/loops');
|
|
34
34
|
const { getSessionStatus, showSessionStatus, flushContext, clearContext, installHooks: sessionInstallHooks, getFlushedContext, installFlushCommand, installAll: sessionInstallAll, SESSION_DIR, FLUSHED_CONTEXT_FILE } = require('./lib/sessions');
|
|
@@ -208,6 +208,11 @@ class ClaudeConfigManager {
|
|
|
208
208
|
workstreamCdHookStatus() { return workstreamCdHookStatus(); }
|
|
209
209
|
generateRulesFromRepos(projects) { return generateRulesFromRepos(projects); }
|
|
210
210
|
generateRulesWithClaude(projects) { return generateRulesWithClaude(projects); }
|
|
211
|
+
generateRulesWithAI(projects, toolId, options) { return generateRulesWithAI(projects, toolId, options); }
|
|
212
|
+
getAvailableAITools() { return getAvailableAITools(); }
|
|
213
|
+
findAIBinary(toolId) { return findAIBinary(toolId); }
|
|
214
|
+
get AI_TOOLS() { return AI_TOOLS; }
|
|
215
|
+
discoverSubProjects(rootPath, maxDepth) { return discoverSubProjects(rootPath, maxDepth); }
|
|
211
216
|
|
|
212
217
|
// Loops (Ralph Loop)
|
|
213
218
|
getLoopsPath() { return getLoopsPath(this.installDir); }
|
package/lib/constants.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Constants and tool path configurations
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const VERSION = '0.
|
|
5
|
+
const VERSION = '0.46.0-beta.7';
|
|
6
6
|
|
|
7
7
|
// Tool-specific path configurations
|
|
8
8
|
const TOOL_PATHS = {
|
|
@@ -10,14 +10,16 @@ const TOOL_PATHS = {
|
|
|
10
10
|
name: 'Claude Code',
|
|
11
11
|
icon: 'sparkles',
|
|
12
12
|
color: 'orange',
|
|
13
|
-
|
|
13
|
+
// Global MCPs are embedded in ~/.claude.json under mcpServers key (no separate file)
|
|
14
|
+
globalConfig: '~/.claude.json',
|
|
14
15
|
globalSettings: '~/.claude/settings.json',
|
|
16
|
+
globalMcpKey: 'mcpServers', // MCPs are under this key in globalConfig
|
|
15
17
|
projectFolder: '.claude',
|
|
16
18
|
projectRules: '.claude/rules',
|
|
17
19
|
projectCommands: '.claude/commands',
|
|
18
20
|
projectWorkflows: '.claude/workflows',
|
|
19
21
|
projectInstructions: 'CLAUDE.md',
|
|
20
|
-
outputFile: '.mcp.json',
|
|
22
|
+
outputFile: '.mcp.json', // Per-project MCPs go here (shared via git)
|
|
21
23
|
supportsEnvInterpolation: true,
|
|
22
24
|
},
|
|
23
25
|
gemini: {
|
package/lib/workstreams.js
CHANGED
|
@@ -688,34 +688,130 @@ function workstreamCheckPath(installDir, targetPath, silent = false) {
|
|
|
688
688
|
}
|
|
689
689
|
|
|
690
690
|
/**
|
|
691
|
-
*
|
|
692
|
-
* Runs `claude -p` to analyze repos and generate smart context
|
|
691
|
+
* Supported AI tools for context generation
|
|
693
692
|
*/
|
|
694
|
-
|
|
693
|
+
const AI_TOOLS = {
|
|
694
|
+
claude: {
|
|
695
|
+
name: 'Claude',
|
|
696
|
+
binary: 'claude',
|
|
697
|
+
buildArgs: (prompt) => ['-p', prompt],
|
|
698
|
+
candidates: (os) => [
|
|
699
|
+
path.join(os.homedir(), '.local', 'bin', 'claude'),
|
|
700
|
+
'/usr/local/bin/claude',
|
|
701
|
+
'/opt/homebrew/bin/claude',
|
|
702
|
+
path.join(os.homedir(), '.npm-global', 'bin', 'claude'),
|
|
703
|
+
],
|
|
704
|
+
},
|
|
705
|
+
gemini: {
|
|
706
|
+
name: 'Gemini',
|
|
707
|
+
binary: 'gemini',
|
|
708
|
+
buildArgs: (prompt) => ['-p', prompt],
|
|
709
|
+
candidates: (os) => [
|
|
710
|
+
path.join(os.homedir(), '.local', 'bin', 'gemini'),
|
|
711
|
+
'/usr/local/bin/gemini',
|
|
712
|
+
'/opt/homebrew/bin/gemini',
|
|
713
|
+
path.join(os.homedir(), '.npm-global', 'bin', 'gemini'),
|
|
714
|
+
],
|
|
715
|
+
},
|
|
716
|
+
codex: {
|
|
717
|
+
name: 'Codex',
|
|
718
|
+
binary: 'codex',
|
|
719
|
+
buildArgs: (prompt) => ['exec', prompt],
|
|
720
|
+
candidates: (os) => [
|
|
721
|
+
path.join(os.homedir(), '.local', 'bin', 'codex'),
|
|
722
|
+
'/usr/local/bin/codex',
|
|
723
|
+
'/opt/homebrew/bin/codex',
|
|
724
|
+
path.join(os.homedir(), '.npm-global', 'bin', 'codex'),
|
|
725
|
+
],
|
|
726
|
+
},
|
|
727
|
+
ollama: {
|
|
728
|
+
name: 'Ollama',
|
|
729
|
+
binary: 'ollama',
|
|
730
|
+
// Model must be specified in options
|
|
731
|
+
buildArgs: (prompt, options) => ['run', options.model || 'llama3.2', prompt],
|
|
732
|
+
candidates: (os) => [
|
|
733
|
+
path.join(os.homedir(), '.local', 'bin', 'ollama'),
|
|
734
|
+
'/usr/local/bin/ollama',
|
|
735
|
+
'/opt/homebrew/bin/ollama',
|
|
736
|
+
],
|
|
737
|
+
},
|
|
738
|
+
aider: {
|
|
739
|
+
name: 'Aider',
|
|
740
|
+
binary: 'aider',
|
|
741
|
+
buildArgs: (prompt) => ['--message', prompt, '--yes', '--no-git'],
|
|
742
|
+
candidates: (os) => [
|
|
743
|
+
path.join(os.homedir(), '.local', 'bin', 'aider'),
|
|
744
|
+
'/usr/local/bin/aider',
|
|
745
|
+
'/opt/homebrew/bin/aider',
|
|
746
|
+
path.join(os.homedir(), '.local', 'pipx', 'venvs', 'aider-chat', 'bin', 'aider'),
|
|
747
|
+
],
|
|
748
|
+
},
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Find the binary path for an AI tool
|
|
753
|
+
*/
|
|
754
|
+
function findAIBinary(toolId) {
|
|
755
|
+
const { execFileSync } = require('child_process');
|
|
756
|
+
const os = require('os');
|
|
757
|
+
|
|
758
|
+
const tool = AI_TOOLS[toolId];
|
|
759
|
+
if (!tool) {
|
|
760
|
+
throw new Error(`Unknown AI tool: ${toolId}`);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// Check candidate paths
|
|
764
|
+
for (const p of tool.candidates(os)) {
|
|
765
|
+
if (fs.existsSync(p)) return p;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Try which command
|
|
769
|
+
try {
|
|
770
|
+
const resolved = execFileSync('which', [tool.binary], { encoding: 'utf8' }).trim();
|
|
771
|
+
if (resolved && fs.existsSync(resolved)) return resolved;
|
|
772
|
+
} catch (e) {}
|
|
773
|
+
|
|
774
|
+
// Fall back to bare binary name (let shell resolve it)
|
|
775
|
+
return tool.binary;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Get list of available AI tools (ones that are installed)
|
|
780
|
+
*/
|
|
781
|
+
function getAvailableAITools() {
|
|
782
|
+
const available = [];
|
|
783
|
+
for (const [id, tool] of Object.entries(AI_TOOLS)) {
|
|
784
|
+
try {
|
|
785
|
+
const binaryPath = findAIBinary(id);
|
|
786
|
+
if (fs.existsSync(binaryPath)) {
|
|
787
|
+
available.push({ id, name: tool.name, path: binaryPath });
|
|
788
|
+
}
|
|
789
|
+
} catch (e) {
|
|
790
|
+
// Tool not available
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return available;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Generate rules/context from project repositories using an AI tool
|
|
798
|
+
* Supports: claude, gemini, codex, ollama, aider
|
|
799
|
+
* @param {string[]} projects - Array of project paths
|
|
800
|
+
* @param {string} toolId - AI tool to use (default: 'claude')
|
|
801
|
+
* @param {object} options - Tool-specific options (e.g., { model: 'llama3.2' } for ollama)
|
|
802
|
+
*/
|
|
803
|
+
async function generateRulesWithAI(projects, toolId = 'claude', options = {}) {
|
|
695
804
|
if (!projects || projects.length === 0) {
|
|
696
805
|
return '';
|
|
697
806
|
}
|
|
698
807
|
|
|
699
808
|
const { execFileSync } = require('child_process');
|
|
700
|
-
const os = require('os');
|
|
701
809
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
'/opt/homebrew/bin/claude',
|
|
708
|
-
path.join(os.homedir(), '.npm-global', 'bin', 'claude'),
|
|
709
|
-
];
|
|
710
|
-
for (const p of candidates) {
|
|
711
|
-
if (fs.existsSync(p)) return p;
|
|
712
|
-
}
|
|
713
|
-
try {
|
|
714
|
-
const resolved = execFileSync('which', ['claude'], { encoding: 'utf8' }).trim();
|
|
715
|
-
if (resolved && fs.existsSync(resolved)) return resolved;
|
|
716
|
-
} catch (e) {}
|
|
717
|
-
return 'claude';
|
|
718
|
-
};
|
|
810
|
+
const tool = AI_TOOLS[toolId];
|
|
811
|
+
if (!tool) {
|
|
812
|
+
console.error(`Unknown AI tool: ${toolId}. Available: ${Object.keys(AI_TOOLS).join(', ')}`);
|
|
813
|
+
return generateRulesFromRepos(projects);
|
|
814
|
+
}
|
|
719
815
|
|
|
720
816
|
// Expand projects to include discovered sub-projects
|
|
721
817
|
const allProjects = [];
|
|
@@ -728,7 +824,7 @@ async function generateRulesWithClaude(projects) {
|
|
|
728
824
|
allProjects.push(absPath);
|
|
729
825
|
}
|
|
730
826
|
|
|
731
|
-
// Discover sub-projects
|
|
827
|
+
// Discover sub-projects
|
|
732
828
|
const subProjects = discoverSubProjects(absPath);
|
|
733
829
|
for (const subPath of subProjects) {
|
|
734
830
|
if (!seen.has(subPath)) {
|
|
@@ -752,23 +848,34 @@ ${projectList}
|
|
|
752
848
|
Output markdown suitable for injecting into an AI assistant's context. Keep it concise (under 500 words). Do not include code blocks or examples - just descriptions and guidelines.`;
|
|
753
849
|
|
|
754
850
|
try {
|
|
755
|
-
|
|
756
|
-
const
|
|
757
|
-
|
|
851
|
+
const binaryPath = findAIBinary(toolId);
|
|
852
|
+
const args = tool.buildArgs(prompt, options);
|
|
853
|
+
|
|
854
|
+
console.log(`Generating context with ${tool.name}...`);
|
|
855
|
+
|
|
856
|
+
const result = execFileSync(binaryPath, args, {
|
|
758
857
|
cwd: allProjects[0],
|
|
759
858
|
encoding: 'utf8',
|
|
760
|
-
timeout:
|
|
859
|
+
timeout: 120000, // 2 minute timeout (some models are slower)
|
|
761
860
|
maxBuffer: 1024 * 1024, // 1MB buffer
|
|
762
861
|
});
|
|
763
862
|
|
|
764
863
|
return result.trim();
|
|
765
864
|
} catch (error) {
|
|
766
|
-
console.error(
|
|
865
|
+
console.error(`${tool.name} generation failed:`, error.message);
|
|
767
866
|
// Fall back to simple generation
|
|
768
867
|
return generateRulesFromRepos(projects);
|
|
769
868
|
}
|
|
770
869
|
}
|
|
771
870
|
|
|
871
|
+
/**
|
|
872
|
+
* Generate rules/context from project repositories using Claude Code
|
|
873
|
+
* @deprecated Use generateRulesWithAI(projects, 'claude') instead
|
|
874
|
+
*/
|
|
875
|
+
async function generateRulesWithClaude(projects) {
|
|
876
|
+
return generateRulesWithAI(projects, 'claude');
|
|
877
|
+
}
|
|
878
|
+
|
|
772
879
|
/**
|
|
773
880
|
* Discover sub-projects within a directory
|
|
774
881
|
* Looks for directories containing project markers (package.json, pyproject.toml, etc.)
|
|
@@ -1460,6 +1567,10 @@ module.exports = {
|
|
|
1460
1567
|
discoverSubProjects,
|
|
1461
1568
|
generateRulesFromRepos,
|
|
1462
1569
|
generateRulesWithClaude,
|
|
1570
|
+
generateRulesWithAI,
|
|
1571
|
+
getAvailableAITools,
|
|
1572
|
+
findAIBinary,
|
|
1573
|
+
AI_TOOLS,
|
|
1463
1574
|
// New folder auto-activation functions
|
|
1464
1575
|
getSettingsPath,
|
|
1465
1576
|
loadSettings,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coder-config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.0-beta.7",
|
|
4
4
|
"description": "Configuration manager for AI coding tools - Claude Code, Gemini CLI, Codex CLI, Antigravity. Manage MCPs, rules, permissions, memory, and workstreams.",
|
|
5
5
|
"author": "regression.io",
|
|
6
6
|
"main": "config-loader.js",
|
package/scripts/release.sh
CHANGED
|
@@ -1,45 +1,78 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
3
|
-
# Usage:
|
|
4
|
-
#
|
|
5
|
-
#
|
|
2
|
+
# Stable release script
|
|
3
|
+
# Usage: npm run release # Interactive, prompts for version
|
|
4
|
+
# npm run release -- 0.46.0 # Specific version
|
|
5
|
+
# npm run release -- --minor # Bump minor version
|
|
6
|
+
# npm run release -- --major # Bump major version
|
|
6
7
|
|
|
7
8
|
set -e
|
|
8
9
|
|
|
9
|
-
if [ -z "$1" ]; then
|
|
10
|
-
echo "Usage: $0 \"commit message\" [--minor|--major]"
|
|
11
|
-
exit 1
|
|
12
|
-
fi
|
|
13
|
-
|
|
14
|
-
MESSAGE="$1"
|
|
15
|
-
BUMP_TYPE="${2:---patch}"
|
|
16
|
-
|
|
17
10
|
# Get current version
|
|
18
|
-
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
|
11
|
+
CURRENT_VERSION=$(node -p "require('./package.json').version.split('-')[0]")
|
|
19
12
|
|
|
20
|
-
# Parse version
|
|
13
|
+
# Parse current version
|
|
21
14
|
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
|
|
22
15
|
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
16
|
+
# Determine new version
|
|
17
|
+
if [ -n "$1" ]; then
|
|
18
|
+
case "$1" in
|
|
19
|
+
--major)
|
|
20
|
+
MAJOR=$((MAJOR + 1))
|
|
21
|
+
MINOR=0
|
|
22
|
+
PATCH=0
|
|
23
|
+
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
|
|
24
|
+
;;
|
|
25
|
+
--minor)
|
|
26
|
+
MINOR=$((MINOR + 1))
|
|
27
|
+
PATCH=0
|
|
28
|
+
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
|
|
29
|
+
;;
|
|
30
|
+
--patch)
|
|
31
|
+
PATCH=$((PATCH + 1))
|
|
32
|
+
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
|
|
33
|
+
;;
|
|
34
|
+
*)
|
|
35
|
+
# Assume it's a version number
|
|
36
|
+
NEW_VERSION="$1"
|
|
37
|
+
;;
|
|
38
|
+
esac
|
|
39
|
+
else
|
|
40
|
+
# Interactive mode
|
|
41
|
+
echo ""
|
|
42
|
+
echo "Current version: $CURRENT_VERSION"
|
|
43
|
+
echo ""
|
|
44
|
+
echo "Enter new version (or press Enter for patch bump):"
|
|
45
|
+
read -r INPUT_VERSION
|
|
46
|
+
|
|
47
|
+
if [ -z "$INPUT_VERSION" ]; then
|
|
35
48
|
PATCH=$((PATCH + 1))
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
|
|
50
|
+
else
|
|
51
|
+
NEW_VERSION="$INPUT_VERSION"
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# Validate version format
|
|
56
|
+
if ! [[ "$NEW_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
57
|
+
echo "Invalid version format: $NEW_VERSION"
|
|
58
|
+
echo "Expected format: X.Y.Z (e.g., 0.46.0)"
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
38
61
|
|
|
39
|
-
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
|
|
40
62
|
TAG="v$NEW_VERSION"
|
|
41
63
|
|
|
42
|
-
echo "
|
|
64
|
+
echo ""
|
|
65
|
+
echo "📦 Releasing: $CURRENT_VERSION -> $NEW_VERSION"
|
|
66
|
+
echo ""
|
|
67
|
+
|
|
68
|
+
# Check for uncommitted changes
|
|
69
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
70
|
+
echo "⚠️ You have uncommitted changes:"
|
|
71
|
+
git status --short
|
|
72
|
+
echo ""
|
|
73
|
+
echo "Commit them first, or press Enter to continue anyway:"
|
|
74
|
+
read -r
|
|
75
|
+
fi
|
|
43
76
|
|
|
44
77
|
# Update version in package.json
|
|
45
78
|
node -e "
|
|
@@ -52,20 +85,22 @@ fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
|
|
|
52
85
|
# Sync version to other files
|
|
53
86
|
npm run version:sync
|
|
54
87
|
|
|
55
|
-
# Stage
|
|
56
|
-
git add
|
|
88
|
+
# Stage version files
|
|
89
|
+
git add package.json lib/constants.js ui/package.json
|
|
57
90
|
|
|
58
|
-
# Commit
|
|
59
|
-
git commit -m "$
|
|
91
|
+
# Commit (skip hooks)
|
|
92
|
+
SKIP_AUTO_PUSH=1 git commit -m "release: v$NEW_VERSION"
|
|
60
93
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
# Tag
|
|
94
|
+
# Create tag
|
|
64
95
|
git tag "$TAG"
|
|
65
96
|
|
|
66
|
-
# Push
|
|
97
|
+
# Push commit and tag
|
|
98
|
+
echo ""
|
|
99
|
+
echo "🚀 Pushing $TAG..."
|
|
67
100
|
git push && git push --tags
|
|
68
101
|
|
|
69
102
|
echo ""
|
|
70
103
|
echo "✅ Released $TAG"
|
|
71
|
-
echo "
|
|
104
|
+
echo ""
|
|
105
|
+
echo "CI will publish to npm with 'latest' tag."
|
|
106
|
+
echo "Users will get this version with: npm install -g coder-config"
|