codiedev 0.5.1 → 0.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.
- package/dist/cli.js +8 -0
- package/dist/commands/delete.d.ts +1 -0
- package/dist/commands/delete.js +96 -0
- package/dist/commands/push.js +38 -8
- package/dist/mcp.js +105 -15
- package/dist/utils.js +17 -7
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
const connect_1 = require("./connect");
|
|
19
19
|
const push_1 = require("./commands/push");
|
|
20
|
+
const delete_1 = require("./commands/delete");
|
|
20
21
|
const pull_1 = require("./commands/pull");
|
|
21
22
|
const ping_1 = require("./commands/ping");
|
|
22
23
|
const inbox_1 = require("./commands/inbox");
|
|
@@ -39,6 +40,9 @@ Artifacts:
|
|
|
39
40
|
codiedev push <file.md> Author or update an artifact
|
|
40
41
|
codiedev pull <key> [--version N] [--out path]
|
|
41
42
|
Fetch an artifact (stdout by default)
|
|
43
|
+
codiedev delete <key> [-y] Delete every version of an artifact you
|
|
44
|
+
authored (clean up bad/test pushes).
|
|
45
|
+
Asks for confirmation; -y to skip.
|
|
42
46
|
codiedev promote <artifact-id> Promote an auto-extracted artifact
|
|
43
47
|
to an authored one
|
|
44
48
|
codiedev reverse-ticket [<pr-url>] Generate a Jira ticket draft.
|
|
@@ -363,6 +367,10 @@ async function main() {
|
|
|
363
367
|
case "push":
|
|
364
368
|
await (0, push_1.runPush)(rest);
|
|
365
369
|
return;
|
|
370
|
+
case "delete":
|
|
371
|
+
case "rm":
|
|
372
|
+
await (0, delete_1.runDelete)(rest);
|
|
373
|
+
return;
|
|
366
374
|
case "pull":
|
|
367
375
|
await (0, pull_1.runPull)(rest);
|
|
368
376
|
return;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runDelete(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runDelete = runDelete;
|
|
37
|
+
const readline = __importStar(require("readline"));
|
|
38
|
+
const shared_1 = require("./shared");
|
|
39
|
+
function parseArgs(args) {
|
|
40
|
+
let key;
|
|
41
|
+
let yes = false;
|
|
42
|
+
for (const a of args) {
|
|
43
|
+
if (a === "-y" || a === "--yes") {
|
|
44
|
+
yes = true;
|
|
45
|
+
}
|
|
46
|
+
else if (!a.startsWith("--") && !key) {
|
|
47
|
+
key = a;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (!key) {
|
|
51
|
+
console.error("Usage: codiedev delete <key> [-y]");
|
|
52
|
+
console.error("Example: codiedev delete spec-old.md");
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
return { key, yes };
|
|
56
|
+
}
|
|
57
|
+
async function confirm(prompt) {
|
|
58
|
+
const rl = readline.createInterface({
|
|
59
|
+
input: process.stdin,
|
|
60
|
+
output: process.stdout,
|
|
61
|
+
});
|
|
62
|
+
const answer = await new Promise((resolve) => {
|
|
63
|
+
rl.question(prompt, (ans) => resolve(ans));
|
|
64
|
+
});
|
|
65
|
+
rl.close();
|
|
66
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
67
|
+
}
|
|
68
|
+
async function runDelete(args) {
|
|
69
|
+
const { key, yes } = parseArgs(args);
|
|
70
|
+
const config = (0, shared_1.requireConfig)();
|
|
71
|
+
if (!yes) {
|
|
72
|
+
const ok = await confirm(`Delete every version of '${key}' (and its replies)? This cannot be undone. [y/N] `);
|
|
73
|
+
if (!ok) {
|
|
74
|
+
console.log("Cancelled.");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const res = await (0, shared_1.apiRequest)("POST", "/api/cli/delete", {
|
|
80
|
+
config,
|
|
81
|
+
body: { key },
|
|
82
|
+
});
|
|
83
|
+
if (res.deleted === 0) {
|
|
84
|
+
console.log(`No artifact found with key '${key}'. Nothing to delete.`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const cascaded = res.cascadedMessages > 0
|
|
88
|
+
? ` + ${res.cascadedMessages} reply${res.cascadedMessages === 1 ? "" : "ies"}`
|
|
89
|
+
: "";
|
|
90
|
+
console.log(`✓ Deleted ${res.deleted} version${res.deleted === 1 ? "" : "s"}${cascaded} of '${res.key}'.`);
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
console.error(`Delete failed: ${err.message}`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
}
|
package/dist/commands/push.js
CHANGED
|
@@ -45,9 +45,13 @@ const VALID_TYPES = new Set([
|
|
|
45
45
|
"review",
|
|
46
46
|
"note",
|
|
47
47
|
]);
|
|
48
|
+
const VALID_KINDS = new Set(["doc", "skill"]);
|
|
48
49
|
function parseArgs(args) {
|
|
49
50
|
let file;
|
|
50
51
|
let type;
|
|
52
|
+
let kind;
|
|
53
|
+
let force = false;
|
|
54
|
+
const tags = [];
|
|
51
55
|
for (let i = 0; i < args.length; i++) {
|
|
52
56
|
const a = args[i];
|
|
53
57
|
if (a === "--type" && i + 1 < args.length) {
|
|
@@ -56,22 +60,41 @@ function parseArgs(args) {
|
|
|
56
60
|
else if (a.startsWith("--type=")) {
|
|
57
61
|
type = a.slice("--type=".length);
|
|
58
62
|
}
|
|
63
|
+
else if (a === "--kind" && i + 1 < args.length) {
|
|
64
|
+
kind = args[++i];
|
|
65
|
+
}
|
|
66
|
+
else if (a.startsWith("--kind=")) {
|
|
67
|
+
kind = a.slice("--kind=".length);
|
|
68
|
+
}
|
|
69
|
+
else if (a === "--tag" && i + 1 < args.length) {
|
|
70
|
+
tags.push(args[++i]);
|
|
71
|
+
}
|
|
72
|
+
else if (a.startsWith("--tag=")) {
|
|
73
|
+
tags.push(a.slice("--tag=".length));
|
|
74
|
+
}
|
|
75
|
+
else if (a === "--force") {
|
|
76
|
+
force = true;
|
|
77
|
+
}
|
|
59
78
|
else if (!a.startsWith("--") && !file) {
|
|
60
79
|
file = a;
|
|
61
80
|
}
|
|
62
81
|
}
|
|
63
82
|
if (!file) {
|
|
64
|
-
console.error("Usage: codiedev push <file.md> [--
|
|
83
|
+
console.error("Usage: codiedev push <file.md> [--kind doc|skill] [--tag <name>] [--force]");
|
|
65
84
|
process.exit(1);
|
|
66
85
|
}
|
|
67
86
|
if (type && !VALID_TYPES.has(type)) {
|
|
68
87
|
console.error(`Invalid --type. Valid types: ${[...VALID_TYPES].join(", ")}`);
|
|
69
88
|
process.exit(1);
|
|
70
89
|
}
|
|
71
|
-
|
|
90
|
+
if (kind && !VALID_KINDS.has(kind)) {
|
|
91
|
+
console.error(`Invalid --kind. Valid kinds: ${[...VALID_KINDS].join(", ")}`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
return { file, type, kind, tags, force };
|
|
72
95
|
}
|
|
73
96
|
async function runPush(args) {
|
|
74
|
-
const { file, type } = parseArgs(args);
|
|
97
|
+
const { file, type, kind, tags, force } = parseArgs(args);
|
|
75
98
|
const config = (0, shared_1.requireConfig)();
|
|
76
99
|
let absolute = file;
|
|
77
100
|
if (!path.isAbsolute(absolute)) {
|
|
@@ -90,12 +113,19 @@ async function runPush(args) {
|
|
|
90
113
|
try {
|
|
91
114
|
const res = await (0, shared_1.apiRequest)("POST", "/api/cli/push", {
|
|
92
115
|
config,
|
|
93
|
-
body: {
|
|
116
|
+
body: {
|
|
117
|
+
filename,
|
|
118
|
+
markdown,
|
|
119
|
+
type,
|
|
120
|
+
kind,
|
|
121
|
+
tags: tags.length > 0 ? tags : undefined,
|
|
122
|
+
force,
|
|
123
|
+
},
|
|
94
124
|
});
|
|
95
|
-
|
|
96
|
-
console.log(
|
|
97
|
-
console.log(`
|
|
98
|
-
console.log(`
|
|
125
|
+
const tagSummary = res.tags && res.tags.length > 0 ? ` tags=[${res.tags.join(", ")}]` : "";
|
|
126
|
+
console.log(`✓ Pushed ${filename} (kind=${res.kind}${tagSummary}).`);
|
|
127
|
+
console.log(` Visible under: ${res.destination}`);
|
|
128
|
+
console.log(` Pull with: codiedev pull ${filename}`);
|
|
99
129
|
}
|
|
100
130
|
catch (err) {
|
|
101
131
|
console.error(`Push failed: ${err.message}`);
|
package/dist/mcp.js
CHANGED
|
@@ -60,31 +60,56 @@ const PKG_VERSION = "0.5.0";
|
|
|
60
60
|
const TOOLS = [
|
|
61
61
|
{
|
|
62
62
|
name: "codiedev_push",
|
|
63
|
-
description: "Push
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
63
|
+
description: "Push any markdown artifact (doc or skill) to the team's CodieDev " +
|
|
64
|
+
"artifact layer so teammates can see, discuss, and run it. Works for " +
|
|
65
|
+
"specs, decisions, proposals, bugfixes, reviews, notes, AND " +
|
|
66
|
+
"Claude Code / Codex skills (files with SKILL.md frontmatter — " +
|
|
67
|
+
"`name:` and `description:` keys). Just pass the filename + markdown " +
|
|
68
|
+
"— `kind` is auto-detected from the content (skill if frontmatter, " +
|
|
69
|
+
"otherwise doc). Use when the user asks to share, push, save, or " +
|
|
70
|
+
"publish something (e.g., 'push this spec', 'save as a skill', " +
|
|
71
|
+
"'share the review'). The response says where the artifact will " +
|
|
72
|
+
"appear so you can confirm to the user.",
|
|
71
73
|
inputSchema: {
|
|
72
74
|
type: "object",
|
|
73
75
|
properties: {
|
|
74
76
|
filename: {
|
|
75
77
|
type: "string",
|
|
76
|
-
description: "Basename of the artifact file, e.g. 'spec-214.md'
|
|
77
|
-
"artifact's key for pulling
|
|
78
|
+
description: "Basename of the artifact file, e.g. 'spec-214.md' or " +
|
|
79
|
+
"'writing-emails.md'. Becomes the artifact's key for pulling " +
|
|
80
|
+
"and pinging.",
|
|
78
81
|
},
|
|
79
82
|
markdown: {
|
|
80
83
|
type: "string",
|
|
81
84
|
description: "Full markdown content of the artifact.",
|
|
82
85
|
},
|
|
86
|
+
kind: {
|
|
87
|
+
type: "string",
|
|
88
|
+
enum: ["doc", "skill"],
|
|
89
|
+
description: "Top-level kind. Auto-detected from frontmatter — leave unset " +
|
|
90
|
+
"in the common case. Pass `skill` for files with SKILL.md " +
|
|
91
|
+
"frontmatter, `doc` for everything else. If you pass a value " +
|
|
92
|
+
"that disagrees with the detected kind the push fails unless " +
|
|
93
|
+
"`force: true` is also set.",
|
|
94
|
+
},
|
|
95
|
+
tags: {
|
|
96
|
+
type: "array",
|
|
97
|
+
items: { type: "string" },
|
|
98
|
+
description: "Optional free-form tags (e.g., ['spec', 'auth']). The legacy " +
|
|
99
|
+
"type prefix from the filename is added automatically — you " +
|
|
100
|
+
"don't need to repeat it.",
|
|
101
|
+
},
|
|
102
|
+
force: {
|
|
103
|
+
type: "boolean",
|
|
104
|
+
description: "Override the kind-mismatch guard. Only pass when the user " +
|
|
105
|
+
"explicitly wants to ignore the detected kind.",
|
|
106
|
+
},
|
|
83
107
|
type: {
|
|
84
108
|
type: "string",
|
|
85
109
|
enum: ["spec", "bugfix", "decision", "proposal", "review", "note"],
|
|
86
|
-
description: "
|
|
87
|
-
"
|
|
110
|
+
description: "Legacy override for the doc-shape tag. Optional — inferred " +
|
|
111
|
+
"from the filename prefix when omitted. Prefer `tags` for new " +
|
|
112
|
+
"code.",
|
|
88
113
|
},
|
|
89
114
|
},
|
|
90
115
|
required: ["filename", "markdown"],
|
|
@@ -112,6 +137,26 @@ const TOOLS = [
|
|
|
112
137
|
required: ["key"],
|
|
113
138
|
},
|
|
114
139
|
},
|
|
140
|
+
{
|
|
141
|
+
name: "codiedev_delete",
|
|
142
|
+
description: "Delete every version of an artifact (and its replies) by key. Use " +
|
|
143
|
+
"when the user asks to delete, remove, undo, throw away, or clean up " +
|
|
144
|
+
"an artifact (e.g., 'delete that test push', 'remove the duplicate', " +
|
|
145
|
+
"'rm spec-old'). Authorship is enforced server-side: you can only " +
|
|
146
|
+
"delete artifacts you authored. Always confirm with the user before " +
|
|
147
|
+
"calling — deletions cannot be undone.",
|
|
148
|
+
inputSchema: {
|
|
149
|
+
type: "object",
|
|
150
|
+
properties: {
|
|
151
|
+
key: {
|
|
152
|
+
type: "string",
|
|
153
|
+
description: "Filename key of the artifact to delete, e.g. 'spec-214.md'. " +
|
|
154
|
+
"Every version of this key is removed.",
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
required: ["key"],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
115
160
|
{
|
|
116
161
|
name: "codiedev_ping",
|
|
117
162
|
description: "Send a message to a teammate on CodieDev, optionally attached to a " +
|
|
@@ -489,6 +534,8 @@ async function dispatchTool(name, args, config) {
|
|
|
489
534
|
return await handlePush(args, config);
|
|
490
535
|
case "codiedev_pull":
|
|
491
536
|
return await handlePull(args, config);
|
|
537
|
+
case "codiedev_delete":
|
|
538
|
+
return await handleDelete(args, config);
|
|
492
539
|
case "codiedev_ping":
|
|
493
540
|
return await handlePing(args, config);
|
|
494
541
|
case "codiedev_inbox":
|
|
@@ -522,20 +569,28 @@ async function handlePush(args, config) {
|
|
|
522
569
|
const filename = asString(args.filename);
|
|
523
570
|
const markdown = asString(args.markdown);
|
|
524
571
|
const type = asStringOrUndefined(args.type);
|
|
572
|
+
const kind = asStringOrUndefined(args.kind);
|
|
573
|
+
const tags = asStringArrayOrUndefined(args.tags);
|
|
574
|
+
const force = asBoolOrUndefined(args.force);
|
|
525
575
|
if (!filename)
|
|
526
576
|
throw new Error("filename required");
|
|
527
577
|
if (!markdown)
|
|
528
578
|
throw new Error("markdown required");
|
|
579
|
+
if (kind && kind !== "doc" && kind !== "skill") {
|
|
580
|
+
throw new Error("kind must be 'doc' or 'skill' if provided");
|
|
581
|
+
}
|
|
529
582
|
const res = await (0, shared_1.apiRequest)("POST", "/api/cli/push", {
|
|
530
583
|
config,
|
|
531
|
-
body: { filename, markdown, type },
|
|
584
|
+
body: { filename, markdown, type, kind, tags, force },
|
|
532
585
|
});
|
|
586
|
+
const tagSummary = res.tags && res.tags.length > 0 ? `, tags=[${res.tags.join(", ")}]` : "";
|
|
533
587
|
return {
|
|
534
588
|
content: [
|
|
535
589
|
{
|
|
536
590
|
type: "text",
|
|
537
|
-
text: `✓ Pushed ${filename} (
|
|
538
|
-
`
|
|
591
|
+
text: `✓ Pushed ${filename} (kind=${res.kind}${tagSummary}, version=v${res.version}, id=${res.artifactId}).\n` +
|
|
592
|
+
` Visible under: ${res.destination}\n` +
|
|
593
|
+
` Pull with: \`codiedev pull ${filename}\``,
|
|
539
594
|
},
|
|
540
595
|
],
|
|
541
596
|
};
|
|
@@ -554,6 +609,33 @@ async function handlePull(args, config) {
|
|
|
554
609
|
content: [{ type: "text", text: header + a.markdown }],
|
|
555
610
|
};
|
|
556
611
|
}
|
|
612
|
+
async function handleDelete(args, config) {
|
|
613
|
+
const key = asString(args.key);
|
|
614
|
+
if (!key)
|
|
615
|
+
throw new Error("key required");
|
|
616
|
+
const res = await (0, shared_1.apiRequest)("POST", "/api/cli/delete", { config, body: { key } });
|
|
617
|
+
if (res.deleted === 0) {
|
|
618
|
+
return {
|
|
619
|
+
content: [
|
|
620
|
+
{
|
|
621
|
+
type: "text",
|
|
622
|
+
text: `No artifact found with key '${key}'. Nothing to delete.`,
|
|
623
|
+
},
|
|
624
|
+
],
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
const cascaded = res.cascadedMessages > 0
|
|
628
|
+
? ` + ${res.cascadedMessages} reply${res.cascadedMessages === 1 ? "" : "ies"}`
|
|
629
|
+
: "";
|
|
630
|
+
return {
|
|
631
|
+
content: [
|
|
632
|
+
{
|
|
633
|
+
type: "text",
|
|
634
|
+
text: `✓ Deleted ${res.deleted} version${res.deleted === 1 ? "" : "s"}${cascaded} of '${res.key}'.`,
|
|
635
|
+
},
|
|
636
|
+
],
|
|
637
|
+
};
|
|
638
|
+
}
|
|
557
639
|
async function handlePing(args, config) {
|
|
558
640
|
const to = asString(args.to);
|
|
559
641
|
const body = asString(args.body);
|
|
@@ -998,6 +1080,14 @@ function asStringOrUndefined(v) {
|
|
|
998
1080
|
function asIntOrUndefined(v) {
|
|
999
1081
|
return typeof v === "number" && Number.isInteger(v) ? v : undefined;
|
|
1000
1082
|
}
|
|
1083
|
+
function asBoolOrUndefined(v) {
|
|
1084
|
+
return typeof v === "boolean" ? v : undefined;
|
|
1085
|
+
}
|
|
1086
|
+
function asStringArrayOrUndefined(v) {
|
|
1087
|
+
if (!Array.isArray(v))
|
|
1088
|
+
return undefined;
|
|
1089
|
+
return v.filter((x) => typeof x === "string");
|
|
1090
|
+
}
|
|
1001
1091
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1002
1092
|
// Main
|
|
1003
1093
|
// ─────────────────────────────────────────────────────────────────────────────
|
package/dist/utils.js
CHANGED
|
@@ -208,6 +208,7 @@ thought, use the \`codiedev\` CLI via Bash:**
|
|
|
208
208
|
|---|---|
|
|
209
209
|
| "push this spec" / "save this as a skill" | \`codiedev push <file.md>\` |
|
|
210
210
|
| "pull X" / "grab the latest spec-X" / "what did Maya push?" | \`codiedev pull <key>\` |
|
|
211
|
+
| "delete X" / "remove that test push" / "rm spec-old" | \`codiedev delete <key>\` (always confirm with the user first) |
|
|
211
212
|
| "ping Nic about this" / "ask Maya for her take" | \`codiedev ping <name> "<msg>" [--with <key>]\` |
|
|
212
213
|
| "any messages?" / "check my inbox" / "what did Nic say?" | \`codiedev inbox\` |
|
|
213
214
|
| "read Nic's reply" / "mark that ping read" | \`codiedev read <ping-id>\` |
|
|
@@ -220,13 +221,22 @@ thought, use the \`codiedev\` CLI via Bash:**
|
|
|
220
221
|
| "show my library" / "what artifacts exist?" | MCP tool \`codiedev_get_library\` |
|
|
221
222
|
| "react 🛠 to that post" / "mark as used" | MCP tool \`codiedev_react\` |
|
|
222
223
|
|
|
223
|
-
**
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
-
|
|
224
|
+
**Just push the file — we'll figure out where it goes.** Every artifact
|
|
225
|
+
is classified into one of two kinds at upload time:
|
|
226
|
+
|
|
227
|
+
- **skill** — the file has SKILL.md frontmatter (\`name:\` + \`description:\`
|
|
228
|
+
inside a \`---\` block). Lands under \`Artifacts → Skills tab\` and is
|
|
229
|
+
invokable as a slash command (\`/<name>\`).
|
|
230
|
+
- **doc** — everything else. Lands under \`Artifacts → My artifacts\`.
|
|
231
|
+
|
|
232
|
+
Detection is content-based, not filename-based. The filename prefix
|
|
233
|
+
(\`spec-\` / \`decision-\` / \`proposal-\` / \`bugfix-\` / \`review-\` /
|
|
234
|
+
\`note-\`) is preserved as a *tag* you can filter by, but it never gates
|
|
235
|
+
visibility. A SKILL-frontmatter file named \`note-foo.md\` still becomes
|
|
236
|
+
a skill.
|
|
237
|
+
|
|
238
|
+
The \`codiedev push\` response always says where the artifact landed —
|
|
239
|
+
read it back to the user so they can confirm.
|
|
230
240
|
|
|
231
241
|
**Resolving "the spec we just worked on":**
|
|
232
242
|
- If a file was recently edited in this session matching the conventions
|