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/LICENSE +21 -21
- package/README.md +227 -207
- package/bin/install.mjs +183 -133
- package/dist/copilot-instructions.md +187 -187
- package/dist/extension.mjs +112 -19
- package/dist/skills/loop/SKILL.md +88 -89
- package/dist/version.json +3 -0
- package/package.json +48 -44
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
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
case "--
|
|
52
|
-
case "-
|
|
53
|
-
flags.
|
|
54
|
-
break;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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);
|