copilot-tap-extension 0.2.0 → 1.0.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/bin/install.mjs CHANGED
@@ -1,133 +1,183 @@
1
- #!/usr/bin/env node
2
- import { existsSync, mkdirSync, copyFileSync } from "node:fs";
3
- import { fileURLToPath } from "node:url";
4
- import path from "node:path";
5
- import os from "node:os";
6
-
7
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
- const pkgRoot = path.resolve(__dirname, "..");
9
- const distDir = path.join(pkgRoot, "dist");
10
-
11
- const BRAND = "※ tap";
12
- const EXT_DIR_NAME = "tap";
13
-
14
- function usage() {
15
- console.log(`
16
- ${BRAND} Copilot CLI extension installer
17
-
18
- Usage:
19
- npx copilot-tap-extension [options]
20
-
21
- Options:
22
- --global, -g Install to ~/.copilot/ (default)
23
- --local, -l Install to .github/ (project-scoped)
24
- --force, -f Overwrite existing files without prompting
25
- --help, -h Show this help message
26
-
27
- Installs:
28
- extensions/tap/extension.mjs The bundled ※ tap extension
29
- skills/loop/SKILL.md The /loop skill for prompt-based loops
30
- copilot-instructions.md Agent instructions for using tap
31
- `);
32
- }
33
-
34
- function parseArgs(argv) {
35
- const args = argv.slice(2);
36
- const flags = { scope: "global", force: false, help: false };
37
- for (const arg of args) {
38
- switch (arg) {
39
- case "--global":
40
- case "-g":
41
- flags.scope = "global";
42
- break;
43
- case "--local":
44
- case "-l":
45
- flags.scope = "local";
46
- break;
47
- case "--force":
48
- case "-f":
49
- flags.force = true;
50
- break;
51
- case "--help":
52
- case "-h":
53
- flags.help = true;
54
- break;
55
- default:
56
- console.error(`Unknown option: ${arg}`);
57
- usage();
58
- process.exit(1);
59
- }
60
- }
61
- return flags;
62
- }
63
-
64
- function getTargetRoot(scope) {
65
- if (scope === "global") {
66
- return path.join(os.homedir(), ".copilot");
67
- }
68
- return path.join(process.cwd(), ".github");
69
- }
70
-
71
- function copyArtifact(src, dest, label, flags) {
72
- if (!existsSync(src)) {
73
- console.error(` ${label}: source not found (${src})`);
74
- return false;
75
- }
76
- if (existsSync(dest) && !flags.force) {
77
- console.log(` ⊘ ${label}: already exists, skipping (use --force to overwrite)`);
78
- return true;
79
- }
80
- mkdirSync(path.dirname(dest), { recursive: true });
81
- copyFileSync(src, dest);
82
- console.log(` ✓ ${label}`);
83
- return true;
84
- }
85
-
86
- function install(flags) {
87
- const targetRoot = getTargetRoot(flags.scope);
88
- const scopeLabel = flags.scope === "global" ? "global (~/.copilot)" : "local (.github)";
89
-
90
- console.log(`\n${BRAND} installing (${scopeLabel})\n`);
91
-
92
- const artifacts = [
93
- {
94
- src: path.join(distDir, "extension.mjs"),
95
- dest: path.join(targetRoot, "extensions", EXT_DIR_NAME, "extension.mjs"),
96
- label: "extensions/tap/extension.mjs"
97
- },
98
- {
99
- src: path.join(distDir, "skills", "loop", "SKILL.md"),
100
- dest: path.join(targetRoot, "skills", "loop", "SKILL.md"),
101
- label: "skills/loop/SKILL.md"
102
- },
103
- {
104
- src: path.join(distDir, "copilot-instructions.md"),
105
- dest: path.join(targetRoot, "copilot-instructions.md"),
106
- label: "copilot-instructions.md"
107
- }
108
- ];
109
-
110
- let allOk = true;
111
- for (const { src, dest, label } of artifacts) {
112
- if (!copyArtifact(src, dest, label, flags)) {
113
- allOk = false;
114
- }
115
- }
116
-
117
- console.log();
118
- if (allOk) {
119
- console.log(`✓ ${BRAND} installed to ${targetRoot}`);
120
- } else {
121
- console.error(`⚠ Some artifacts could not be installed.`);
122
- process.exit(1);
123
- }
124
- }
125
-
126
- const flags = parseArgs(process.argv);
127
-
128
- if (flags.help) {
129
- usage();
130
- process.exit(0);
131
- }
132
-
133
- install(flags);
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, copyFileSync, readFileSync } from "node:fs";
3
+ import { fileURLToPath } from "node:url";
4
+ import path from "node:path";
5
+ import os from "node:os";
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+ const pkgRoot = path.resolve(__dirname, "..");
9
+ const distDir = path.join(pkgRoot, "dist");
10
+
11
+ const BRAND = "※ tap";
12
+ const EXT_DIR_NAME = "tap";
13
+
14
+ function getPackageVersion() {
15
+ try {
16
+ return JSON.parse(readFileSync(path.join(distDir, "version.json"), "utf8")).version;
17
+ } catch {
18
+ return JSON.parse(readFileSync(path.join(pkgRoot, "package.json"), "utf8")).version;
19
+ }
20
+ }
21
+
22
+ function usage() {
23
+ console.log(`
24
+ ${BRAND} Copilot CLI extension installer
25
+
26
+ Usage:
27
+ npx copilot-tap-extension [options]
28
+
29
+ If tap is already installed, updates core files (extension + version)
30
+ and preserves customizable artifacts. If fresh, does a full install.
31
+
32
+ Options:
33
+ --global, -g Install to ~/.copilot/ (default)
34
+ --local, -l Install to .github/ (project-scoped)
35
+ --full Force a full install even if already installed
36
+ --help, -h Show this help message
37
+
38
+ Installs:
39
+ extensions/tap/extension.mjs The bundled ※ tap extension
40
+ extensions/tap/version.json Installed version metadata
41
+ skills/loop/SKILL.md The /loop skill for prompt-based loops
42
+ copilot-instructions.md Agent instructions for using ※ tap
43
+ `);
44
+ }
45
+
46
+ function parseArgs(argv) {
47
+ const args = argv.slice(2);
48
+ const flags = { scope: "global", full: false, help: false };
49
+ for (const arg of args) {
50
+ switch (arg) {
51
+ case "--global":
52
+ case "-g":
53
+ flags.scope = "global";
54
+ break;
55
+ case "--local":
56
+ case "-l":
57
+ flags.scope = "local";
58
+ break;
59
+ case "--full":
60
+ flags.full = true;
61
+ break;
62
+ // Keep legacy flags working
63
+ case "--force":
64
+ case "-f":
65
+ case "--update":
66
+ case "-u":
67
+ break;
68
+ case "--help":
69
+ case "-h":
70
+ flags.help = true;
71
+ break;
72
+ default:
73
+ console.error(`Unknown option: ${arg}`);
74
+ usage();
75
+ process.exit(1);
76
+ }
77
+ }
78
+ return flags;
79
+ }
80
+
81
+ function getTargetRoot(scope) {
82
+ if (scope === "global") {
83
+ return path.join(os.homedir(), ".copilot");
84
+ }
85
+ return path.join(process.cwd(), ".github");
86
+ }
87
+
88
+ function copyArtifact(src, dest, label) {
89
+ if (!existsSync(src)) {
90
+ console.error(` ✗ ${label}: source not found (${src})`);
91
+ return false;
92
+ }
93
+ mkdirSync(path.dirname(dest), { recursive: true });
94
+ copyFileSync(src, dest);
95
+ console.log(` ✓ ${label}`);
96
+ return true;
97
+ }
98
+
99
+ function getInstalledVersion(targetRoot) {
100
+ try {
101
+ const versionFile = path.join(targetRoot, "extensions", EXT_DIR_NAME, "version.json");
102
+ return JSON.parse(readFileSync(versionFile, "utf8")).version;
103
+ } catch {
104
+ return null;
105
+ }
106
+ }
107
+
108
+ function isAlreadyInstalled(targetRoot) {
109
+ return existsSync(path.join(targetRoot, "extensions", EXT_DIR_NAME, "extension.mjs"));
110
+ }
111
+
112
+ function install(flags) {
113
+ const targetRoot = getTargetRoot(flags.scope);
114
+ const scopeLabel = flags.scope === "global" ? "global (~/.copilot)" : "local (.github)";
115
+ const packageVersion = getPackageVersion();
116
+ const installed = isAlreadyInstalled(targetRoot);
117
+ const isUpdate = installed && !flags.full;
118
+
119
+ if (isUpdate) {
120
+ const installedVersion = getInstalledVersion(targetRoot);
121
+ if (installedVersion && installedVersion === packageVersion) {
122
+ console.log(`\n${BRAND} — already up to date (v${installedVersion})\n`);
123
+ process.exit(0);
124
+ }
125
+ const fromLabel = installedVersion ? `v${installedVersion}` : "unknown";
126
+ console.log(`\n${BRAND} updating ${fromLabel} → v${packageVersion} (${scopeLabel})\n`);
127
+ } else {
128
+ console.log(`\n${BRAND} — installing v${packageVersion} (${scopeLabel})\n`);
129
+ }
130
+
131
+ const coreArtifacts = [
132
+ {
133
+ src: path.join(distDir, "extension.mjs"),
134
+ dest: path.join(targetRoot, "extensions", EXT_DIR_NAME, "extension.mjs"),
135
+ label: "extensions/tap/extension.mjs"
136
+ },
137
+ {
138
+ src: path.join(distDir, "version.json"),
139
+ dest: path.join(targetRoot, "extensions", EXT_DIR_NAME, "version.json"),
140
+ label: "extensions/tap/version.json"
141
+ }
142
+ ];
143
+
144
+ const ancillaryArtifacts = [
145
+ {
146
+ src: path.join(distDir, "skills", "loop", "SKILL.md"),
147
+ dest: path.join(targetRoot, "skills", "loop", "SKILL.md"),
148
+ label: "skills/loop/SKILL.md"
149
+ },
150
+ {
151
+ src: path.join(distDir, "copilot-instructions.md"),
152
+ dest: path.join(targetRoot, "copilot-instructions.md"),
153
+ label: "copilot-instructions.md"
154
+ }
155
+ ];
156
+
157
+ const artifacts = isUpdate ? coreArtifacts : [...coreArtifacts, ...ancillaryArtifacts];
158
+
159
+ let allOk = true;
160
+ for (const { src, dest, label } of artifacts) {
161
+ if (!copyArtifact(src, dest, label)) {
162
+ allOk = false;
163
+ }
164
+ }
165
+
166
+ console.log();
167
+ if (allOk) {
168
+ const verb = isUpdate ? "updated" : "installed";
169
+ console.log(`✓ ${BRAND} ${verb} to ${targetRoot}`);
170
+ } else {
171
+ console.error(`⚠ Some artifacts could not be ${isUpdate ? "updated" : "installed"}.`);
172
+ process.exit(1);
173
+ }
174
+ }
175
+
176
+ const flags = parseArgs(process.argv);
177
+
178
+ if (flags.help) {
179
+ usage();
180
+ process.exit(0);
181
+ }
182
+
183
+ install(flags);