@hacksmith/doraval 0.2.35 → 0.2.43
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/doraval.js +1387 -453
- package/bin/ui/index.html +732 -0
- package/package.json +2 -2
package/bin/doraval.js
CHANGED
|
@@ -599,7 +599,7 @@ var init_dist = __esm(() => {
|
|
|
599
599
|
var require_package = __commonJS((exports, module) => {
|
|
600
600
|
module.exports = {
|
|
601
601
|
name: "@hacksmith/doraval",
|
|
602
|
-
version: "0.2.
|
|
602
|
+
version: "0.2.43",
|
|
603
603
|
author: "Saif",
|
|
604
604
|
repository: {
|
|
605
605
|
type: "git",
|
|
@@ -640,7 +640,7 @@ var require_package = __commonJS((exports, module) => {
|
|
|
640
640
|
"apps/*"
|
|
641
641
|
],
|
|
642
642
|
scripts: {
|
|
643
|
-
build: "bun build ./src/cli/index.ts --outfile ./bin/doraval.js --target bun",
|
|
643
|
+
build: "bun build ./src/cli/index.ts --outfile ./bin/doraval.js --target bun && rm -rf bin/ui && cp -r src/ui bin/ui",
|
|
644
644
|
dev: "bun run ./src/cli/index.ts",
|
|
645
645
|
test: "bun test",
|
|
646
646
|
typecheck: "bunx tsc --noEmit --skipLibCheck",
|
|
@@ -906,7 +906,7 @@ var init_validate = __esm(() => {
|
|
|
906
906
|
validate_default = defineCommand({
|
|
907
907
|
meta: {
|
|
908
908
|
name: "validate",
|
|
909
|
-
description: "Validate structure and schema of a skill or plugin"
|
|
909
|
+
description: "Validate structure and schema of a skill or plugin (keywords in plugin.json help agent discovery)"
|
|
910
910
|
},
|
|
911
911
|
args: {
|
|
912
912
|
path: {
|
|
@@ -1817,20 +1817,483 @@ var init_list = __esm(() => {
|
|
|
1817
1817
|
});
|
|
1818
1818
|
});
|
|
1819
1819
|
|
|
1820
|
+
// src/cli/commands/journal/context.ts
|
|
1821
|
+
var exports_context = {};
|
|
1822
|
+
__export(exports_context, {
|
|
1823
|
+
generateJournalContext: () => generateJournalContext,
|
|
1824
|
+
default: () => context_default
|
|
1825
|
+
});
|
|
1826
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1827
|
+
import { join as join4, resolve as resolvePath } from "path";
|
|
1828
|
+
function truncate(text, max = 180) {
|
|
1829
|
+
const clean = text.replace(/\s+/g, " ").trim();
|
|
1830
|
+
if (clean.length <= max)
|
|
1831
|
+
return clean;
|
|
1832
|
+
return clean.slice(0, max - 1) + "\u2026";
|
|
1833
|
+
}
|
|
1834
|
+
function groupByPushback(entries) {
|
|
1835
|
+
const strong = [];
|
|
1836
|
+
const friction = [];
|
|
1837
|
+
const nudge = [];
|
|
1838
|
+
for (const e of entries) {
|
|
1839
|
+
const pb = e.pushback ?? 0;
|
|
1840
|
+
if (pb >= 7)
|
|
1841
|
+
strong.push(e);
|
|
1842
|
+
else if (pb >= 4)
|
|
1843
|
+
friction.push(e);
|
|
1844
|
+
else
|
|
1845
|
+
nudge.push(e);
|
|
1846
|
+
}
|
|
1847
|
+
const byPush = (a, b) => (b.pushback ?? 0) - (a.pushback ?? 0);
|
|
1848
|
+
return {
|
|
1849
|
+
strong: strong.sort(byPush),
|
|
1850
|
+
friction: friction.sort(byPush),
|
|
1851
|
+
nudge: nudge.sort(byPush)
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
function generateJournalContext(entries, project, opts = {}) {
|
|
1855
|
+
const minPb = opts.minPushback ?? 1;
|
|
1856
|
+
const showFull = !!opts.full;
|
|
1857
|
+
const includeGlobal = !opts.noGlobal;
|
|
1858
|
+
let filtered = entries.filter((e) => {
|
|
1859
|
+
const pb = e.pushback ?? 0;
|
|
1860
|
+
const isActive = (e.status || "active") === "active";
|
|
1861
|
+
return isActive && pb >= minPb;
|
|
1862
|
+
});
|
|
1863
|
+
if (!includeGlobal) {}
|
|
1864
|
+
if (filtered.length === 0) {
|
|
1865
|
+
return "";
|
|
1866
|
+
}
|
|
1867
|
+
const groups = groupByPushback(filtered);
|
|
1868
|
+
let out = "";
|
|
1869
|
+
const scope = project ? `for ${project}` : "from your journal";
|
|
1870
|
+
out += `These are recorded decisions and principles ${scope}. `;
|
|
1871
|
+
out += `Higher pushback entries represent stronger prior commitments.
|
|
1872
|
+
|
|
1873
|
+
`;
|
|
1874
|
+
const renderGroup = (title, items) => {
|
|
1875
|
+
if (items.length === 0)
|
|
1876
|
+
return "";
|
|
1877
|
+
let g = `## ${title}
|
|
1878
|
+
|
|
1879
|
+
`;
|
|
1880
|
+
for (const e of items) {
|
|
1881
|
+
const tags = (e.tags || []).length ? `tags: ${(e.tags || []).join(", ")}` : "";
|
|
1882
|
+
const pb = e.pushback ?? 0;
|
|
1883
|
+
g += `- **${e.title}** (pushback: ${pb}${tags ? `, ${tags}` : ""})
|
|
1884
|
+
`;
|
|
1885
|
+
const body = showFull ? (e.rationale || "").trim() : truncate(e.rationale || "");
|
|
1886
|
+
if (body) {
|
|
1887
|
+
g += ` ${body}
|
|
1888
|
+
`;
|
|
1889
|
+
}
|
|
1890
|
+
g += `
|
|
1891
|
+
`;
|
|
1892
|
+
}
|
|
1893
|
+
return g;
|
|
1894
|
+
};
|
|
1895
|
+
out += renderGroup("Strong (pushback 7\u201310)", groups.strong);
|
|
1896
|
+
out += renderGroup("Friction (pushback 4\u20136)", groups.friction);
|
|
1897
|
+
out += renderGroup("Nudges (pushback 1\u20133)", groups.nudge);
|
|
1898
|
+
out += "If these feel out of date, run: `dora journal update` or `dora journal list`.\n";
|
|
1899
|
+
return out.trimEnd() + `
|
|
1900
|
+
`;
|
|
1901
|
+
}
|
|
1902
|
+
async function appendOrUpdateJournalBlock(target, contextText, project, useReference) {
|
|
1903
|
+
const absTarget = resolvePath(process.cwd(), target);
|
|
1904
|
+
let original = "";
|
|
1905
|
+
if (existsSync5(absTarget)) {
|
|
1906
|
+
original = await Bun.file(absTarget).text();
|
|
1907
|
+
}
|
|
1908
|
+
let blockContent;
|
|
1909
|
+
if (useReference) {
|
|
1910
|
+
const refLines = [];
|
|
1911
|
+
refLines.push("## Recorded decisions (from journal)");
|
|
1912
|
+
refLines.push("");
|
|
1913
|
+
refLines.push("@~/.doraval/journals/global.md");
|
|
1914
|
+
if (project) {
|
|
1915
|
+
refLines.push(`@~/.doraval/journals/${project}.md`);
|
|
1916
|
+
}
|
|
1917
|
+
refLines.push("");
|
|
1918
|
+
refLines.push("_These are your synced project decisions. High pushback items are strong commitments._");
|
|
1919
|
+
blockContent = refLines.join(`
|
|
1920
|
+
`);
|
|
1921
|
+
} else {
|
|
1922
|
+
blockContent = contextText.trim();
|
|
1923
|
+
}
|
|
1924
|
+
const newBlock = [
|
|
1925
|
+
JOURNAL_BLOCK_START,
|
|
1926
|
+
"",
|
|
1927
|
+
blockContent,
|
|
1928
|
+
"",
|
|
1929
|
+
JOURNAL_BLOCK_END
|
|
1930
|
+
].join(`
|
|
1931
|
+
`);
|
|
1932
|
+
let updated;
|
|
1933
|
+
const startIdx = original.indexOf(JOURNAL_BLOCK_START);
|
|
1934
|
+
const endIdx = original.indexOf(JOURNAL_BLOCK_END);
|
|
1935
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
1936
|
+
const before = original.slice(0, startIdx);
|
|
1937
|
+
const after = original.slice(endIdx + JOURNAL_BLOCK_END.length);
|
|
1938
|
+
updated = before + newBlock + after;
|
|
1939
|
+
} else {
|
|
1940
|
+
const separator = original.trim().length > 0 ? `
|
|
1941
|
+
|
|
1942
|
+
` : "";
|
|
1943
|
+
updated = original + separator + newBlock + `
|
|
1944
|
+
`;
|
|
1945
|
+
}
|
|
1946
|
+
await Bun.write(absTarget, updated);
|
|
1947
|
+
const action = existsSync5(absTarget) && startIdx !== -1 ? "Updated" : "Added";
|
|
1948
|
+
ui.write(`
|
|
1949
|
+
${import_picocolors7.default.green("\u2713")} ${action} journal decisions section in ${import_picocolors7.default.white(target)}`);
|
|
1950
|
+
if (useReference) {
|
|
1951
|
+
ui.write(` ${import_picocolors7.default.dim("Using @import references (full files will be loaded by Claude).")}`);
|
|
1952
|
+
} else {
|
|
1953
|
+
ui.write(` ${import_picocolors7.default.dim("Embedded compact decisions (low noise).")}`);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
var import_picocolors7, JOURNAL_BLOCK_START = "<!-- doraval-journal:start -->", JOURNAL_BLOCK_END = "<!-- doraval-journal:end -->", context_default;
|
|
1957
|
+
var init_context = __esm(() => {
|
|
1958
|
+
init_dist();
|
|
1959
|
+
init_out();
|
|
1960
|
+
init_journal_config();
|
|
1961
|
+
init_journal_parse();
|
|
1962
|
+
import_picocolors7 = __toESM(require_picocolors(), 1);
|
|
1963
|
+
context_default = defineCommand({
|
|
1964
|
+
meta: {
|
|
1965
|
+
name: "context",
|
|
1966
|
+
description: "Output compact journal decisions (for hooks, CLAUDE.md, or piping)"
|
|
1967
|
+
},
|
|
1968
|
+
args: {
|
|
1969
|
+
project: {
|
|
1970
|
+
type: "string",
|
|
1971
|
+
alias: "p",
|
|
1972
|
+
description: "Project name (defaults to directory-based mapping)"
|
|
1973
|
+
},
|
|
1974
|
+
"min-pushback": {
|
|
1975
|
+
type: "string",
|
|
1976
|
+
description: "Only include entries with at least this pushback (1-10)",
|
|
1977
|
+
default: "1"
|
|
1978
|
+
},
|
|
1979
|
+
full: {
|
|
1980
|
+
type: "boolean",
|
|
1981
|
+
description: "Show full rationale instead of truncated",
|
|
1982
|
+
default: false
|
|
1983
|
+
},
|
|
1984
|
+
"no-global": {
|
|
1985
|
+
type: "boolean",
|
|
1986
|
+
description: "Exclude global entries (project only)",
|
|
1987
|
+
default: false
|
|
1988
|
+
},
|
|
1989
|
+
"append-to": {
|
|
1990
|
+
type: "string",
|
|
1991
|
+
description: "Append (or update) a managed section in this file (e.g. CLAUDE.md or AGENTS.md)"
|
|
1992
|
+
},
|
|
1993
|
+
reference: {
|
|
1994
|
+
type: "boolean",
|
|
1995
|
+
description: "When appending, use @import references instead of embedding compact decisions",
|
|
1996
|
+
default: false
|
|
1997
|
+
},
|
|
1998
|
+
"print-hook": {
|
|
1999
|
+
type: "boolean",
|
|
2000
|
+
description: "Print a ready-to-paste SessionStart hook snippet for hooks.json",
|
|
2001
|
+
default: false
|
|
2002
|
+
}
|
|
2003
|
+
},
|
|
2004
|
+
async run({ args }) {
|
|
2005
|
+
if (args["print-hook"]) {
|
|
2006
|
+
const hookCmd = "sh -c 'dora journal context 2>/dev/null || true'";
|
|
2007
|
+
console.log(JSON.stringify({
|
|
2008
|
+
SessionStart: [
|
|
2009
|
+
{
|
|
2010
|
+
hooks: [
|
|
2011
|
+
{
|
|
2012
|
+
type: "command",
|
|
2013
|
+
command: hookCmd
|
|
2014
|
+
}
|
|
2015
|
+
]
|
|
2016
|
+
}
|
|
2017
|
+
]
|
|
2018
|
+
}, null, 2));
|
|
2019
|
+
console.error("\nTip: Use `dora journal hook enable` to install the hook automatically.");
|
|
2020
|
+
console.error(" Use `dora journal hook disable` to remove it.");
|
|
2021
|
+
console.error(" (sh -c wrapper ensures shell features like redir work reliably.)");
|
|
2022
|
+
process.exit(0);
|
|
2023
|
+
}
|
|
2024
|
+
const config = await readConfig();
|
|
2025
|
+
let project = args.project;
|
|
2026
|
+
if (!project) {
|
|
2027
|
+
project = resolveProjectName(config) ?? undefined;
|
|
2028
|
+
}
|
|
2029
|
+
if (project) {
|
|
2030
|
+
project = sanitizeProjectName(project);
|
|
2031
|
+
}
|
|
2032
|
+
const journalsDir = getJournalsDir();
|
|
2033
|
+
const entries = [];
|
|
2034
|
+
const globalPath = join4(journalsDir, "global.md");
|
|
2035
|
+
if (existsSync5(globalPath)) {
|
|
2036
|
+
try {
|
|
2037
|
+
const raw = await Bun.file(globalPath).text();
|
|
2038
|
+
entries.push(...parseJournalEntries(raw));
|
|
2039
|
+
} catch {}
|
|
2040
|
+
}
|
|
2041
|
+
if (project) {
|
|
2042
|
+
const projectPath = join4(journalsDir, `${project}.md`);
|
|
2043
|
+
if (existsSync5(projectPath)) {
|
|
2044
|
+
try {
|
|
2045
|
+
const raw = await Bun.file(projectPath).text();
|
|
2046
|
+
entries.push(...parseJournalEntries(raw));
|
|
2047
|
+
} catch {}
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
const minPb = parseInt(String(args["min-pushback"] ?? "1"), 10) || 1;
|
|
2051
|
+
const contextText = generateJournalContext(entries, project ?? null, {
|
|
2052
|
+
minPushback: minPb,
|
|
2053
|
+
full: !!args.full,
|
|
2054
|
+
noGlobal: !!args["no-global"]
|
|
2055
|
+
});
|
|
2056
|
+
const appendTarget = args["append-to"];
|
|
2057
|
+
if (appendTarget && contextText) {
|
|
2058
|
+
await appendOrUpdateJournalBlock(appendTarget, contextText, project ?? null, !!args.reference);
|
|
2059
|
+
}
|
|
2060
|
+
if (contextText && !appendTarget) {
|
|
2061
|
+
console.log(contextText);
|
|
2062
|
+
} else if (contextText && appendTarget) {}
|
|
2063
|
+
process.exit(0);
|
|
2064
|
+
}
|
|
2065
|
+
});
|
|
2066
|
+
});
|
|
2067
|
+
|
|
2068
|
+
// src/cli/commands/journal/hook.ts
|
|
2069
|
+
var exports_hook = {};
|
|
2070
|
+
__export(exports_hook, {
|
|
2071
|
+
writeHookConfig: () => writeJson,
|
|
2072
|
+
removeHook: () => removeHook,
|
|
2073
|
+
readHookConfig: () => readJson,
|
|
2074
|
+
hasHook: () => hasHook,
|
|
2075
|
+
getLocalHooksPath: () => getLocalHooksPath,
|
|
2076
|
+
getGlobalSettingsPath: () => getGlobalSettingsPath,
|
|
2077
|
+
default: () => hook_default,
|
|
2078
|
+
addHook: () => addHook
|
|
2079
|
+
});
|
|
2080
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, unlinkSync, rmdirSync, readdirSync as readdirSync2 } from "fs";
|
|
2081
|
+
import { join as join5, dirname } from "path";
|
|
2082
|
+
import { homedir as homedir2 } from "os";
|
|
2083
|
+
function getGlobalSettingsPath() {
|
|
2084
|
+
return join5(homedir2(), ".claude", "settings.json");
|
|
2085
|
+
}
|
|
2086
|
+
function getLocalHooksPath() {
|
|
2087
|
+
return join5(process.cwd(), "hooks", "hooks.json");
|
|
2088
|
+
}
|
|
2089
|
+
async function readJson(file) {
|
|
2090
|
+
if (!existsSync6(file))
|
|
2091
|
+
return {};
|
|
2092
|
+
try {
|
|
2093
|
+
const raw = await Bun.file(file).text();
|
|
2094
|
+
return JSON.parse(raw);
|
|
2095
|
+
} catch {
|
|
2096
|
+
return {};
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
async function writeJson(file, data) {
|
|
2100
|
+
const dir = dirname(file);
|
|
2101
|
+
if (!existsSync6(dir)) {
|
|
2102
|
+
mkdirSync2(dir, { recursive: true });
|
|
2103
|
+
}
|
|
2104
|
+
await Bun.write(file, JSON.stringify(data, null, 2) + `
|
|
2105
|
+
`);
|
|
2106
|
+
}
|
|
2107
|
+
function hasHook(config) {
|
|
2108
|
+
const sessionStart = config?.hooks?.SessionStart;
|
|
2109
|
+
if (!Array.isArray(sessionStart))
|
|
2110
|
+
return false;
|
|
2111
|
+
return sessionStart.some((group) => Array.isArray(group?.hooks) && group.hooks.some((h) => h?.command === HOOK_COMMAND));
|
|
2112
|
+
}
|
|
2113
|
+
async function addHook(file) {
|
|
2114
|
+
const original = await readJson(file);
|
|
2115
|
+
const config = JSON.parse(JSON.stringify(original));
|
|
2116
|
+
if (!config.hooks)
|
|
2117
|
+
config.hooks = {};
|
|
2118
|
+
if (!Array.isArray(config.hooks.SessionStart)) {
|
|
2119
|
+
config.hooks.SessionStart = [];
|
|
2120
|
+
}
|
|
2121
|
+
if (hasHook(config)) {
|
|
2122
|
+
return { changed: false, path: file };
|
|
2123
|
+
}
|
|
2124
|
+
config.hooks.SessionStart.push(HOOK_GROUP);
|
|
2125
|
+
await writeJson(file, config);
|
|
2126
|
+
return { changed: true, path: file };
|
|
2127
|
+
}
|
|
2128
|
+
async function removeHook(file) {
|
|
2129
|
+
if (!existsSync6(file))
|
|
2130
|
+
return { changed: false, path: file };
|
|
2131
|
+
const original = await readJson(file);
|
|
2132
|
+
const config = JSON.parse(JSON.stringify(original));
|
|
2133
|
+
if (!config.hooks || !Array.isArray(config.hooks.SessionStart)) {
|
|
2134
|
+
return { changed: false, path: file };
|
|
2135
|
+
}
|
|
2136
|
+
const beforeLen = config.hooks.SessionStart.length;
|
|
2137
|
+
config.hooks.SessionStart = config.hooks.SessionStart.map((group) => {
|
|
2138
|
+
if (!group || !Array.isArray(group.hooks))
|
|
2139
|
+
return group;
|
|
2140
|
+
group.hooks = group.hooks.filter((h) => h?.command !== HOOK_COMMAND);
|
|
2141
|
+
return group;
|
|
2142
|
+
}).filter((group) => Array.isArray(group?.hooks) && group.hooks.length > 0);
|
|
2143
|
+
if (config.hooks.SessionStart.length === 0) {
|
|
2144
|
+
delete config.hooks.SessionStart;
|
|
2145
|
+
}
|
|
2146
|
+
if (config.hooks && Object.keys(config.hooks).length === 0) {
|
|
2147
|
+
delete config.hooks;
|
|
2148
|
+
}
|
|
2149
|
+
const changed = JSON.stringify(config) !== JSON.stringify(original);
|
|
2150
|
+
if (changed) {
|
|
2151
|
+
const isEmpty = !config || Object.keys(config).length === 0;
|
|
2152
|
+
if (isEmpty && existsSync6(file)) {
|
|
2153
|
+
try {
|
|
2154
|
+
unlinkSync(file);
|
|
2155
|
+
} catch {}
|
|
2156
|
+
try {
|
|
2157
|
+
const dir = dirname(file);
|
|
2158
|
+
if (existsSync6(dir) && readdirSync2(dir).length === 0)
|
|
2159
|
+
rmdirSync(dir);
|
|
2160
|
+
} catch {}
|
|
2161
|
+
} else {
|
|
2162
|
+
await writeJson(file, config);
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
return { changed, path: file };
|
|
2166
|
+
}
|
|
2167
|
+
async function printHookStatus() {
|
|
2168
|
+
const localPath = getLocalHooksPath();
|
|
2169
|
+
const globalPath = getGlobalSettingsPath();
|
|
2170
|
+
const localHas = hasHook(await readJson(localPath));
|
|
2171
|
+
const globalHas = hasHook(await readJson(globalPath));
|
|
2172
|
+
if (localHas) {
|
|
2173
|
+
ui.success(`Enabled in project: ${localPath}`);
|
|
2174
|
+
}
|
|
2175
|
+
if (globalHas) {
|
|
2176
|
+
ui.success(`Enabled globally: ${globalPath}`);
|
|
2177
|
+
}
|
|
2178
|
+
if (!localHas && !globalHas) {
|
|
2179
|
+
ui.info("Journal hook is not installed.");
|
|
2180
|
+
ui.info("Run `dora journal hook enable` to install it.");
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
var HOOK_COMMAND = "sh -c 'dora journal context 2>/dev/null || true'", HOOK_GROUP, enable, disable, status, hook_default;
|
|
2184
|
+
var init_hook = __esm(() => {
|
|
2185
|
+
init_dist();
|
|
2186
|
+
init_out();
|
|
2187
|
+
HOOK_GROUP = {
|
|
2188
|
+
hooks: [
|
|
2189
|
+
{
|
|
2190
|
+
type: "command",
|
|
2191
|
+
command: HOOK_COMMAND
|
|
2192
|
+
}
|
|
2193
|
+
]
|
|
2194
|
+
};
|
|
2195
|
+
enable = defineCommand({
|
|
2196
|
+
meta: {
|
|
2197
|
+
name: "enable",
|
|
2198
|
+
description: "Install the journal decisions hook (SessionStart) so decisions are injected into Claude sessions"
|
|
2199
|
+
},
|
|
2200
|
+
args: {
|
|
2201
|
+
global: {
|
|
2202
|
+
type: "boolean",
|
|
2203
|
+
alias: "g",
|
|
2204
|
+
description: "Install to global ~/.claude/settings.json (instead of project hooks/hooks.json)",
|
|
2205
|
+
default: false
|
|
2206
|
+
}
|
|
2207
|
+
},
|
|
2208
|
+
async run({ args }) {
|
|
2209
|
+
const useGlobal = !!args.global;
|
|
2210
|
+
const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
|
|
2211
|
+
const result = await addHook(target);
|
|
2212
|
+
if (result.changed) {
|
|
2213
|
+
ui.success(`Enabled journal hook in ${result.path}`);
|
|
2214
|
+
if (useGlobal) {
|
|
2215
|
+
ui.info("Installed globally \u2014 will affect all new Claude sessions (your typical setup with many plugins).");
|
|
2216
|
+
} else {
|
|
2217
|
+
ui.info("Installed locally for this project \u2014 will affect Claude sessions started from this directory.");
|
|
2218
|
+
ui.info("If your Claude hooks live in the global ~/.claude/settings.json (very common), re-run with -g/--global.");
|
|
2219
|
+
}
|
|
2220
|
+
ui.info("Start a new Claude session (or restart) for the hook to take effect.");
|
|
2221
|
+
ui.dim("The hook runs `dora journal context` on SessionStart and injects your active decisions.");
|
|
2222
|
+
ui.info("Preview what gets injected: dora journal context");
|
|
2223
|
+
ui.info("Test inside Claude: ask it to list or recall your recent journal decisions.");
|
|
2224
|
+
} else {
|
|
2225
|
+
ui.info(`Journal hook is already enabled in ${result.path}`);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
});
|
|
2229
|
+
disable = defineCommand({
|
|
2230
|
+
meta: {
|
|
2231
|
+
name: "disable",
|
|
2232
|
+
description: "Remove the journal decisions hook from Claude configuration"
|
|
2233
|
+
},
|
|
2234
|
+
args: {
|
|
2235
|
+
global: {
|
|
2236
|
+
type: "boolean",
|
|
2237
|
+
alias: "g",
|
|
2238
|
+
description: "Remove from global ~/.claude/settings.json",
|
|
2239
|
+
default: false
|
|
2240
|
+
}
|
|
2241
|
+
},
|
|
2242
|
+
async run({ args }) {
|
|
2243
|
+
const useGlobal = !!args.global;
|
|
2244
|
+
const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
|
|
2245
|
+
const result = await removeHook(target);
|
|
2246
|
+
if (result.changed) {
|
|
2247
|
+
ui.success(`Disabled journal hook in ${result.path}`);
|
|
2248
|
+
ui.info("The decisions will no longer be injected on new SessionStart.");
|
|
2249
|
+
} else {
|
|
2250
|
+
ui.info(`Journal hook was not present in ${result.path}`);
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
});
|
|
2254
|
+
status = defineCommand({
|
|
2255
|
+
meta: {
|
|
2256
|
+
name: "status",
|
|
2257
|
+
description: "Check where the journal hook is currently installed"
|
|
2258
|
+
},
|
|
2259
|
+
async run() {
|
|
2260
|
+
await printHookStatus();
|
|
2261
|
+
}
|
|
2262
|
+
});
|
|
2263
|
+
hook_default = defineCommand({
|
|
2264
|
+
meta: {
|
|
2265
|
+
name: "hook",
|
|
2266
|
+
description: "Manage Claude hooks for automatically injecting journal decisions"
|
|
2267
|
+
},
|
|
2268
|
+
subCommands: {
|
|
2269
|
+
enable: () => Promise.resolve(enable),
|
|
2270
|
+
disable: () => Promise.resolve(disable),
|
|
2271
|
+
status: () => Promise.resolve(status)
|
|
2272
|
+
},
|
|
2273
|
+
async run() {
|
|
2274
|
+
const cliArgs = process.argv.slice(2);
|
|
2275
|
+
const hookIdx = cliArgs.indexOf("hook");
|
|
2276
|
+
if (hookIdx !== -1 && cliArgs.length > hookIdx + 1)
|
|
2277
|
+
return;
|
|
2278
|
+
await printHookStatus();
|
|
2279
|
+
}
|
|
2280
|
+
});
|
|
2281
|
+
});
|
|
2282
|
+
|
|
1820
2283
|
// src/cli/commands/journal/update.ts
|
|
1821
2284
|
var exports_update = {};
|
|
1822
2285
|
__export(exports_update, {
|
|
1823
2286
|
default: () => update_default
|
|
1824
2287
|
});
|
|
1825
|
-
import { existsSync as
|
|
1826
|
-
import { join as
|
|
1827
|
-
var
|
|
2288
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2289
|
+
import { join as join6 } from "path";
|
|
2290
|
+
var import_picocolors8, update_default;
|
|
1828
2291
|
var init_update = __esm(() => {
|
|
1829
2292
|
init_dist();
|
|
1830
2293
|
init_out();
|
|
1831
2294
|
init_journal_config();
|
|
1832
2295
|
init_journal_remote();
|
|
1833
|
-
|
|
2296
|
+
import_picocolors8 = __toESM(require_picocolors(), 1);
|
|
1834
2297
|
update_default = defineCommand({
|
|
1835
2298
|
meta: {
|
|
1836
2299
|
name: "update",
|
|
@@ -1851,30 +2314,30 @@ var init_update = __esm(() => {
|
|
|
1851
2314
|
async run({ args }) {
|
|
1852
2315
|
const ghCheck = ensureGhCli();
|
|
1853
2316
|
if (!ghCheck.ok) {
|
|
1854
|
-
ui.write(` ${
|
|
2317
|
+
ui.write(` ${import_picocolors8.default.red("\u2717")} ${import_picocolors8.default.white("The GitHub CLI (")}${import_picocolors8.default.bold("gh")}${import_picocolors8.default.white(") is not installed.")}
|
|
1855
2318
|
`);
|
|
1856
|
-
ui.write(` doraval uses ${
|
|
2319
|
+
ui.write(` doraval uses ${import_picocolors8.default.bold("gh")} to fetch and sync journal files with GitHub.
|
|
1857
2320
|
`);
|
|
1858
2321
|
ui.write(` Install it:
|
|
1859
2322
|
`);
|
|
1860
|
-
ui.write(` macOS: ${
|
|
1861
|
-
ui.write(` Linux: ${
|
|
1862
|
-
ui.write(` Windows: ${
|
|
2323
|
+
ui.write(` macOS: ${import_picocolors8.default.dim("brew install gh")}`);
|
|
2324
|
+
ui.write(` Linux: ${import_picocolors8.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
|
|
2325
|
+
ui.write(` Windows: ${import_picocolors8.default.dim("winget install --id GitHub.cli")}
|
|
1863
2326
|
`);
|
|
1864
|
-
ui.write(` Then authenticate: ${
|
|
2327
|
+
ui.write(` Then authenticate: ${import_picocolors8.default.dim("gh auth login")}
|
|
1865
2328
|
`);
|
|
1866
2329
|
process.exit(1);
|
|
1867
2330
|
}
|
|
1868
2331
|
const config = await readConfig();
|
|
1869
2332
|
if (!config?.journal.repo) {
|
|
1870
|
-
ui.write(`${
|
|
2333
|
+
ui.write(`${import_picocolors8.default.red("\u2717")} No journal repo configured. Run ${import_picocolors8.default.dim("dora init")} (or ${import_picocolors8.default.dim("doraval journal init")}) first.`);
|
|
1871
2334
|
process.exit(1);
|
|
1872
2335
|
}
|
|
1873
2336
|
const journalRepo = config.journal.repo;
|
|
1874
2337
|
ensureDoravalDirs();
|
|
1875
2338
|
const journalsDir = getJournalsDir();
|
|
1876
2339
|
ui.write(`
|
|
1877
|
-
${
|
|
2340
|
+
${import_picocolors8.default.bold(import_picocolors8.default.white("dora journal update"))} \u2014 ${import_picocolors8.default.dim(import_picocolors8.default.gray(journalRepo))}
|
|
1878
2341
|
`);
|
|
1879
2342
|
const projectsToUpdate = [];
|
|
1880
2343
|
if (args.all) {
|
|
@@ -1892,19 +2355,19 @@ var init_update = __esm(() => {
|
|
|
1892
2355
|
try {
|
|
1893
2356
|
projectsToUpdate.push(sanitizeProjectName(project));
|
|
1894
2357
|
} catch {
|
|
1895
|
-
ui.write(`${
|
|
2358
|
+
ui.write(`${import_picocolors8.default.red("\u2717")} Invalid project name: ${project}`);
|
|
1896
2359
|
process.exit(1);
|
|
1897
2360
|
}
|
|
1898
2361
|
}
|
|
1899
2362
|
}
|
|
1900
|
-
const globalLocal =
|
|
2363
|
+
const globalLocal = join6(journalsDir, "global.md");
|
|
1901
2364
|
const refreshGlobalRes = await refreshLocalJournalFile(journalRepo, "global.md", globalLocal);
|
|
1902
2365
|
let gotGlobal;
|
|
1903
2366
|
if (!refreshGlobalRes.ok) {
|
|
1904
2367
|
if (refreshGlobalRes.isNotFound) {
|
|
1905
2368
|
gotGlobal = false;
|
|
1906
2369
|
} else {
|
|
1907
|
-
ui.write(`${
|
|
2370
|
+
ui.write(`${import_picocolors8.default.red("\u2717")} Failed to fetch global.md from ${journalRepo}:`);
|
|
1908
2371
|
ui.write(refreshGlobalRes.error);
|
|
1909
2372
|
process.exit(1);
|
|
1910
2373
|
}
|
|
@@ -1912,33 +2375,33 @@ var init_update = __esm(() => {
|
|
|
1912
2375
|
gotGlobal = refreshGlobalRes.value;
|
|
1913
2376
|
}
|
|
1914
2377
|
if (gotGlobal) {
|
|
1915
|
-
ui.write(` ${
|
|
2378
|
+
ui.write(` ${import_picocolors8.default.green("\u2713")} global.md`);
|
|
1916
2379
|
} else {
|
|
1917
|
-
ui.write(` ${
|
|
2380
|
+
ui.write(` ${import_picocolors8.default.dim("\xB7")} global.md ${import_picocolors8.default.dim("(not present on remote)")}`);
|
|
1918
2381
|
}
|
|
1919
2382
|
if (projectsToUpdate.length === 0) {
|
|
1920
2383
|
if (args.all) {
|
|
1921
2384
|
ui.write(`
|
|
1922
|
-
${
|
|
2385
|
+
${import_picocolors8.default.dim(import_picocolors8.default.gray("No projects registered."))}
|
|
1923
2386
|
`);
|
|
1924
2387
|
} else {
|
|
1925
2388
|
ui.write(`
|
|
1926
|
-
${
|
|
1927
|
-
` + ` Run ${
|
|
2389
|
+
${import_picocolors8.default.yellow("\u26A0")} No project mapping found.
|
|
2390
|
+
` + ` Run ${import_picocolors8.default.dim("dora init")} or pass ${import_picocolors8.default.dim("--project <name>")} / ${import_picocolors8.default.dim("--all")}.
|
|
1928
2391
|
`);
|
|
1929
2392
|
}
|
|
1930
2393
|
return;
|
|
1931
2394
|
}
|
|
1932
2395
|
for (const project of projectsToUpdate) {
|
|
1933
2396
|
const remotePath = `projects/${project}.md`;
|
|
1934
|
-
const localPath =
|
|
2397
|
+
const localPath = join6(journalsDir, `${project}.md`);
|
|
1935
2398
|
const refreshRes = await refreshLocalJournalFile(journalRepo, remotePath, localPath);
|
|
1936
2399
|
let got;
|
|
1937
2400
|
if (!refreshRes.ok) {
|
|
1938
2401
|
if (refreshRes.isNotFound) {
|
|
1939
2402
|
got = false;
|
|
1940
2403
|
} else {
|
|
1941
|
-
ui.write(`${
|
|
2404
|
+
ui.write(`${import_picocolors8.default.red("\u2717")} Failed to fetch ${remotePath} from ${journalRepo}:`);
|
|
1942
2405
|
ui.write(refreshRes.error);
|
|
1943
2406
|
process.exit(1);
|
|
1944
2407
|
}
|
|
@@ -1946,10 +2409,10 @@ var init_update = __esm(() => {
|
|
|
1946
2409
|
got = refreshRes.value;
|
|
1947
2410
|
}
|
|
1948
2411
|
if (got) {
|
|
1949
|
-
ui.write(` ${
|
|
2412
|
+
ui.write(` ${import_picocolors8.default.green("\u2713")} ${remotePath}`);
|
|
1950
2413
|
} else {
|
|
1951
|
-
ui.write(` ${
|
|
1952
|
-
if (!
|
|
2414
|
+
ui.write(` ${import_picocolors8.default.dim("\xB7")} ${remotePath} ${import_picocolors8.default.dim("(not present on remote \u2014 will be created on first sync)")}`);
|
|
2415
|
+
if (!existsSync7(localPath)) {
|
|
1953
2416
|
await Bun.write(localPath, `# ${project} Journal
|
|
1954
2417
|
|
|
1955
2418
|
Project-specific decisions.
|
|
@@ -1959,7 +2422,7 @@ Project-specific decisions.
|
|
|
1959
2422
|
}
|
|
1960
2423
|
const summary = args.all && projectsToUpdate.length > 1 ? `${projectsToUpdate.length} projects + global` : projectsToUpdate.length === 1 ? projectsToUpdate[0] : "journals";
|
|
1961
2424
|
ui.write(`
|
|
1962
|
-
${
|
|
2425
|
+
${import_picocolors8.default.dim(import_picocolors8.default.gray("Local cache refreshed for"))} ${import_picocolors8.default.bold(import_picocolors8.default.white(summary))}.
|
|
1963
2426
|
`);
|
|
1964
2427
|
}
|
|
1965
2428
|
});
|
|
@@ -2026,8 +2489,8 @@ __export(exports_add, {
|
|
|
2026
2489
|
default: () => add_default,
|
|
2027
2490
|
buildAgentArgv: () => buildAgentArgv
|
|
2028
2491
|
});
|
|
2029
|
-
import { existsSync as
|
|
2030
|
-
import { join as
|
|
2492
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2493
|
+
import { join as join7 } from "path";
|
|
2031
2494
|
var {spawnSync: spawnSync2 } = globalThis.Bun;
|
|
2032
2495
|
function buildAgentArgv(template, promptText) {
|
|
2033
2496
|
const marker = "__DORA_PROMPT__";
|
|
@@ -2067,7 +2530,7 @@ If you cannot produce exactly this, output the JSON with the best you can and se
|
|
|
2067
2530
|
const template = agentCfg.prompt_template || '-p "{{prompt}}" --output-format json';
|
|
2068
2531
|
const extraArgs = buildAgentArgv(template, scaffold);
|
|
2069
2532
|
const shortTemplate = (agentCfg.prompt_template || '-p "{{prompt}}" --output-format json').slice(0, 80);
|
|
2070
|
-
ui.write(` ${
|
|
2533
|
+
ui.write(` ${import_picocolors9.default.dim(`\u2192 ${agentCfg.command} ${shortTemplate}...`)}`);
|
|
2071
2534
|
try {
|
|
2072
2535
|
const result = spawnSync2([agentCfg.command, ...extraArgs], {
|
|
2073
2536
|
stdout: "pipe",
|
|
@@ -2076,12 +2539,12 @@ If you cannot produce exactly this, output the JSON with the best you can and se
|
|
|
2076
2539
|
const stdout = result.stdout.toString().trim();
|
|
2077
2540
|
const stderr = result.stderr.toString().trim();
|
|
2078
2541
|
if (result.exitCode !== 0) {
|
|
2079
|
-
ui.write(` ${
|
|
2542
|
+
ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Configured agent (${agentCfg.command}) exited with code ${result.exitCode}. Falling back to defaults.`);
|
|
2080
2543
|
if (stderr)
|
|
2081
|
-
ui.write(` ${
|
|
2544
|
+
ui.write(` ${import_picocolors9.default.dim("stderr:")}
|
|
2082
2545
|
${stderr.slice(0, 800)}`);
|
|
2083
2546
|
if (stdout)
|
|
2084
|
-
ui.write(` ${
|
|
2547
|
+
ui.write(` ${import_picocolors9.default.dim("stdout:")}
|
|
2085
2548
|
${stdout.slice(0, 400)}`);
|
|
2086
2549
|
return null;
|
|
2087
2550
|
}
|
|
@@ -2126,37 +2589,37 @@ ${stdout.slice(0, 400)}`);
|
|
|
2126
2589
|
parsed = candidates[0] || null;
|
|
2127
2590
|
}
|
|
2128
2591
|
if (!parsed || typeof parsed !== "object") {
|
|
2129
|
-
ui.write(` ${
|
|
2130
|
-
ui.write(` ${
|
|
2592
|
+
ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Agent produced output but no usable JSON was found. Falling back.`);
|
|
2593
|
+
ui.write(` ${import_picocolors9.default.dim("stdout (first 700 chars):")}
|
|
2131
2594
|
${stdout.slice(0, 700)}`);
|
|
2132
2595
|
if (stderr)
|
|
2133
|
-
ui.write(` ${
|
|
2596
|
+
ui.write(` ${import_picocolors9.default.dim("stderr:")}
|
|
2134
2597
|
${stderr.slice(0, 500)}`);
|
|
2135
2598
|
return null;
|
|
2136
2599
|
}
|
|
2137
2600
|
if (!parsed.title && !parsed.rationale) {
|
|
2138
|
-
ui.write(` ${
|
|
2139
|
-
ui.write(` ${
|
|
2140
|
-
ui.write(` ${
|
|
2601
|
+
ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Agent returned JSON without expected fields (title/rationale). Using defaults.`);
|
|
2602
|
+
ui.write(` ${import_picocolors9.default.dim("parsed keys:")} ${Object.keys(parsed).join(", ")}`);
|
|
2603
|
+
ui.write(` ${import_picocolors9.default.dim("stdout (truncated):")}
|
|
2141
2604
|
${stdout.slice(0, 600)}`);
|
|
2142
2605
|
return null;
|
|
2143
2606
|
}
|
|
2144
2607
|
return parsed;
|
|
2145
2608
|
} catch (e) {
|
|
2146
|
-
ui.write(` ${
|
|
2609
|
+
ui.write(` ${import_picocolors9.default.yellow("\u26A0")} Failed to invoke configured agent (${agentCfg.command}): ${e.message}. Using defaults.`);
|
|
2147
2610
|
return null;
|
|
2148
2611
|
}
|
|
2149
2612
|
}
|
|
2150
2613
|
function slugify(title) {
|
|
2151
2614
|
return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "untitled";
|
|
2152
2615
|
}
|
|
2153
|
-
var
|
|
2616
|
+
var import_picocolors9, add_default;
|
|
2154
2617
|
var init_add = __esm(() => {
|
|
2155
2618
|
init_dist();
|
|
2156
2619
|
init_out();
|
|
2157
2620
|
init_journal_config();
|
|
2158
2621
|
init_journal_validate();
|
|
2159
|
-
|
|
2622
|
+
import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
2160
2623
|
add_default = defineCommand({
|
|
2161
2624
|
meta: {
|
|
2162
2625
|
name: "add",
|
|
@@ -2231,16 +2694,16 @@ var init_add = __esm(() => {
|
|
|
2231
2694
|
project = sanitizeProjectName(project);
|
|
2232
2695
|
}
|
|
2233
2696
|
if (!project) {
|
|
2234
|
-
ui.write(`${
|
|
2697
|
+
ui.write(`${import_picocolors9.default.yellow("\u26A0")} No project mapping found.
|
|
2235
2698
|
|
|
2236
|
-
` + `Run ${
|
|
2699
|
+
` + `Run ${import_picocolors9.default.dim("dora init")} (or ${import_picocolors9.default.dim("doraval journal init")}) first, or pass ${import_picocolors9.default.dim("--project <name>")}.`);
|
|
2237
2700
|
process.exit(1);
|
|
2238
2701
|
}
|
|
2239
2702
|
let title;
|
|
2240
2703
|
let pushback;
|
|
2241
2704
|
let tags = [];
|
|
2242
2705
|
let author = String(args.author || "human");
|
|
2243
|
-
let
|
|
2706
|
+
let status2 = args.status || "active";
|
|
2244
2707
|
let rationale;
|
|
2245
2708
|
let date = new Date().toISOString().split("T")[0];
|
|
2246
2709
|
const jsonInput = args.json;
|
|
@@ -2267,11 +2730,11 @@ var init_add = __esm(() => {
|
|
|
2267
2730
|
if (parsed.author)
|
|
2268
2731
|
author = String(parsed.author);
|
|
2269
2732
|
if (parsed.status)
|
|
2270
|
-
|
|
2733
|
+
status2 = parsed.status;
|
|
2271
2734
|
if (parsed.date)
|
|
2272
2735
|
date = String(parsed.date);
|
|
2273
2736
|
} catch (e) {
|
|
2274
|
-
ui.write(`${
|
|
2737
|
+
ui.write(`${import_picocolors9.default.red("\u2717")} Failed to parse --json input: ${e.message}`);
|
|
2275
2738
|
process.exit(1);
|
|
2276
2739
|
}
|
|
2277
2740
|
}
|
|
@@ -2280,7 +2743,7 @@ var init_add = __esm(() => {
|
|
|
2280
2743
|
if (rawMdArg && !jsonInput) {
|
|
2281
2744
|
if (rawMdArg === "-" || rawMdArg === "") {
|
|
2282
2745
|
rawBody = (await new Response(Bun.stdin.stream()).text()).trim();
|
|
2283
|
-
} else if (
|
|
2746
|
+
} else if (existsSync8(rawMdArg)) {
|
|
2284
2747
|
rawBody = (await Bun.file(rawMdArg).text()).trim();
|
|
2285
2748
|
} else {
|
|
2286
2749
|
rawBody = rawMdArg.trim();
|
|
@@ -2295,7 +2758,7 @@ var init_add = __esm(() => {
|
|
|
2295
2758
|
title = (headingMatch[1] ?? "").trim();
|
|
2296
2759
|
rawBody = rawBody.replace(/^#+\s+(.+?)(?:\r?\n|$)/m, "").trimStart();
|
|
2297
2760
|
} else {
|
|
2298
|
-
ui.write(`${
|
|
2761
|
+
ui.write(`${import_picocolors9.default.red("\u2717")} --raw-markdown provided without a TITLE and without a leading '# Heading' in the markdown.`);
|
|
2299
2762
|
process.exit(1);
|
|
2300
2763
|
}
|
|
2301
2764
|
}
|
|
@@ -2335,7 +2798,7 @@ var init_add = __esm(() => {
|
|
|
2335
2798
|
agentCfg = fullConfigForAgent?.agent;
|
|
2336
2799
|
if (agentCfg) {
|
|
2337
2800
|
attemptedAgent = true;
|
|
2338
|
-
ui.write(` ${
|
|
2801
|
+
ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("(querying your configured coding agent...)"))}`);
|
|
2339
2802
|
const agentResult = await invokeConfiguredAgentForEntry(title, agentCfg);
|
|
2340
2803
|
if (agentResult) {
|
|
2341
2804
|
if (agentResult.title)
|
|
@@ -2352,7 +2815,7 @@ var init_add = __esm(() => {
|
|
|
2352
2815
|
if (agentResult.author)
|
|
2353
2816
|
author = String(agentResult.author);
|
|
2354
2817
|
if (agentResult.status)
|
|
2355
|
-
|
|
2818
|
+
status2 = agentResult.status;
|
|
2356
2819
|
if (agentResult.date)
|
|
2357
2820
|
date = String(agentResult.date);
|
|
2358
2821
|
}
|
|
@@ -2364,22 +2827,22 @@ var init_add = __esm(() => {
|
|
|
2364
2827
|
tags,
|
|
2365
2828
|
author,
|
|
2366
2829
|
date,
|
|
2367
|
-
status
|
|
2830
|
+
status: status2
|
|
2368
2831
|
};
|
|
2369
2832
|
const validation = validateEntry(entry);
|
|
2370
2833
|
if (!validation.valid) {
|
|
2371
|
-
ui.write(`${
|
|
2834
|
+
ui.write(`${import_picocolors9.default.red("\u2717")} Invalid entry:
|
|
2372
2835
|
`);
|
|
2373
2836
|
for (const err of validation.errors) {
|
|
2374
|
-
ui.write(` ${
|
|
2837
|
+
ui.write(` ${import_picocolors9.default.red("\u2022")} ${err}`);
|
|
2375
2838
|
}
|
|
2376
2839
|
process.exit(1);
|
|
2377
2840
|
}
|
|
2378
2841
|
for (const warn of validation.warnings) {
|
|
2379
2842
|
if ((warn.includes("not supplied") || warn.includes("empty")) && attemptedAgent) {} else if (warn.includes("not supplied") || warn.includes("empty")) {
|
|
2380
|
-
ui.write(`${
|
|
2843
|
+
ui.write(`${import_picocolors9.default.dim("\xB7")} ${warn}`);
|
|
2381
2844
|
} else {
|
|
2382
|
-
ui.write(`${
|
|
2845
|
+
ui.write(`${import_picocolors9.default.yellow("\u26A0")} ${warn}`);
|
|
2383
2846
|
}
|
|
2384
2847
|
}
|
|
2385
2848
|
if (!rationale) {
|
|
@@ -2392,38 +2855,38 @@ pushback: ${pushback}
|
|
|
2392
2855
|
tags: [${tags.join(", ")}]
|
|
2393
2856
|
author: ${author}
|
|
2394
2857
|
date: ${date}
|
|
2395
|
-
status: ${
|
|
2858
|
+
status: ${status2}
|
|
2396
2859
|
\`\`\`
|
|
2397
2860
|
|
|
2398
2861
|
${rationale}
|
|
2399
2862
|
`;
|
|
2400
2863
|
ensureDoravalDirs();
|
|
2401
2864
|
const pendingDir = getPendingProjectDir(project);
|
|
2402
|
-
if (!
|
|
2403
|
-
await Bun.write(
|
|
2865
|
+
if (!existsSync8(pendingDir)) {
|
|
2866
|
+
await Bun.write(join7(pendingDir, ".gitkeep"), "");
|
|
2404
2867
|
}
|
|
2405
2868
|
const slug = slugify(title);
|
|
2406
2869
|
const filename = `${date}-${slug}.md`;
|
|
2407
|
-
const filePath =
|
|
2870
|
+
const filePath = join7(pendingDir, filename);
|
|
2408
2871
|
await Bun.write(filePath, content);
|
|
2409
2872
|
ui.write(`
|
|
2410
|
-
${
|
|
2411
|
-
ui.write(` Project: ${
|
|
2873
|
+
${import_picocolors9.default.green("\u2713")} ${import_picocolors9.default.bold(import_picocolors9.default.white(title))}`);
|
|
2874
|
+
ui.write(` Project: ${import_picocolors9.default.white(project)} \xB7 run ${import_picocolors9.default.dim(import_picocolors9.default.gray("dora journal sync"))} to publish
|
|
2412
2875
|
`);
|
|
2413
2876
|
if (args.verbose) {
|
|
2414
|
-
const authorDisplay = author.startsWith("agent:") ?
|
|
2415
|
-
ui.write(` Pushback: ${
|
|
2416
|
-
ui.write(` Tags: ${
|
|
2877
|
+
const authorDisplay = author.startsWith("agent:") ? import_picocolors9.default.cyan(author) : author;
|
|
2878
|
+
ui.write(` Pushback: ${import_picocolors9.default.white(String(pushback))}`);
|
|
2879
|
+
ui.write(` Tags: ${import_picocolors9.default.gray(tags.join(", ") || import_picocolors9.default.dim("(none)"))}`);
|
|
2417
2880
|
ui.write(` Author: ${authorDisplay}`);
|
|
2418
|
-
ui.write(` File: ${
|
|
2881
|
+
ui.write(` File: ${import_picocolors9.default.dim(import_picocolors9.default.gray(filePath))}
|
|
2419
2882
|
`);
|
|
2420
2883
|
}
|
|
2421
2884
|
if (isThinInput && !author.startsWith("agent:")) {
|
|
2422
2885
|
if (attemptedAgent) {
|
|
2423
|
-
ui.write(` ${
|
|
2886
|
+
ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("Note: agent was called but returned no usable enrichment. Edit the pending file or re-run dora init."))}
|
|
2424
2887
|
`);
|
|
2425
2888
|
} else {
|
|
2426
|
-
ui.write(` ${
|
|
2889
|
+
ui.write(` ${import_picocolors9.default.dim(import_picocolors9.default.gray("Tip: run dora init to configure an agent for auto-enrichment."))}
|
|
2427
2890
|
`);
|
|
2428
2891
|
}
|
|
2429
2892
|
}
|
|
@@ -2437,8 +2900,8 @@ var exports_sync = {};
|
|
|
2437
2900
|
__export(exports_sync, {
|
|
2438
2901
|
default: () => sync_default
|
|
2439
2902
|
});
|
|
2440
|
-
import { readdirSync as
|
|
2441
|
-
import { join as
|
|
2903
|
+
import { readdirSync as readdirSync3, existsSync as existsSync9 } from "fs";
|
|
2904
|
+
import { join as join8 } from "path";
|
|
2442
2905
|
var {spawnSync: spawnSync3 } = globalThis.Bun;
|
|
2443
2906
|
function updateGitHubFile(repo, path, content, message, sha) {
|
|
2444
2907
|
const payload = {
|
|
@@ -2467,18 +2930,18 @@ function updateGitHubFile(repo, path, content, message, sha) {
|
|
|
2467
2930
|
stderr: "pipe"
|
|
2468
2931
|
});
|
|
2469
2932
|
if (result.exitCode !== 0) {
|
|
2470
|
-
ui.write(
|
|
2933
|
+
ui.write(import_picocolors10.default.red(`Failed to update ${path} on ${repo}:`));
|
|
2471
2934
|
ui.write(result.stderr.toString());
|
|
2472
2935
|
process.exit(1);
|
|
2473
2936
|
}
|
|
2474
2937
|
}
|
|
2475
|
-
var
|
|
2938
|
+
var import_picocolors10, sync_default;
|
|
2476
2939
|
var init_sync = __esm(() => {
|
|
2477
2940
|
init_dist();
|
|
2478
2941
|
init_out();
|
|
2479
2942
|
init_journal_config();
|
|
2480
2943
|
init_journal_remote();
|
|
2481
|
-
|
|
2944
|
+
import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
2482
2945
|
sync_default = defineCommand({
|
|
2483
2946
|
meta: {
|
|
2484
2947
|
name: "sync",
|
|
@@ -2512,70 +2975,70 @@ var init_sync = __esm(() => {
|
|
|
2512
2975
|
project = sanitizeProjectName(project);
|
|
2513
2976
|
}
|
|
2514
2977
|
if (!project) {
|
|
2515
|
-
ui.write(`${
|
|
2978
|
+
ui.write(`${import_picocolors10.default.yellow("\u26A0")} No project mapping found.
|
|
2516
2979
|
|
|
2517
|
-
` + `Run ${
|
|
2980
|
+
` + `Run ${import_picocolors10.default.dim("dora init")} (or ${import_picocolors10.default.dim("doraval journal init")}) first, or pass ${import_picocolors10.default.dim("--project <name>")}.`);
|
|
2518
2981
|
process.exit(1);
|
|
2519
2982
|
}
|
|
2520
2983
|
if (!config?.journal.repo) {
|
|
2521
|
-
ui.write(`${
|
|
2984
|
+
ui.write(`${import_picocolors10.default.red("\u2717")} No journal repo configured. Run ${import_picocolors10.default.dim("dora init")} (or ${import_picocolors10.default.dim("doraval journal init")}) first.`);
|
|
2522
2985
|
process.exit(1);
|
|
2523
2986
|
}
|
|
2524
2987
|
const ghCheck = ensureGhCli();
|
|
2525
2988
|
if (!ghCheck.ok) {
|
|
2526
|
-
ui.write(` ${
|
|
2989
|
+
ui.write(` ${import_picocolors10.default.red("\u2717")} ${import_picocolors10.default.white("The GitHub CLI (")}${import_picocolors10.default.bold("gh")}${import_picocolors10.default.white(") is not installed.")}
|
|
2527
2990
|
`);
|
|
2528
|
-
ui.write(` doraval uses ${
|
|
2991
|
+
ui.write(` doraval uses ${import_picocolors10.default.bold("gh")} to fetch and sync journal files with GitHub.
|
|
2529
2992
|
`);
|
|
2530
2993
|
ui.write(` Install it:
|
|
2531
2994
|
`);
|
|
2532
|
-
ui.write(` macOS: ${
|
|
2533
|
-
ui.write(` Linux: ${
|
|
2534
|
-
ui.write(` Windows: ${
|
|
2995
|
+
ui.write(` macOS: ${import_picocolors10.default.dim("brew install gh")}`);
|
|
2996
|
+
ui.write(` Linux: ${import_picocolors10.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
|
|
2997
|
+
ui.write(` Windows: ${import_picocolors10.default.dim("winget install --id GitHub.cli")}
|
|
2535
2998
|
`);
|
|
2536
|
-
ui.write(` Then authenticate: ${
|
|
2999
|
+
ui.write(` Then authenticate: ${import_picocolors10.default.dim("gh auth login")}
|
|
2537
3000
|
`);
|
|
2538
3001
|
process.exit(1);
|
|
2539
3002
|
}
|
|
2540
3003
|
const journalRepo = config.journal.repo;
|
|
2541
3004
|
const pendingDir = getPendingProjectDir(project);
|
|
2542
3005
|
ui.write(`
|
|
2543
|
-
${
|
|
3006
|
+
${import_picocolors10.default.bold(import_picocolors10.default.white("dora journal sync"))} \u2014 ${import_picocolors10.default.white(project)}
|
|
2544
3007
|
`);
|
|
2545
|
-
ui.write(` Journal repo: ${
|
|
3008
|
+
ui.write(` Journal repo: ${import_picocolors10.default.dim(import_picocolors10.default.gray(journalRepo))}`);
|
|
2546
3009
|
ensureDoravalDirs();
|
|
2547
3010
|
const journalsDir = getJournalsDir();
|
|
2548
3011
|
const remoteProjectPath = `projects/${project}.md`;
|
|
2549
|
-
const localProjectPath =
|
|
2550
|
-
ui.write(` ${
|
|
2551
|
-
const refreshGlobalRes = await refreshLocalJournalFile(journalRepo, "global.md",
|
|
3012
|
+
const localProjectPath = join8(journalsDir, `${project}.md`);
|
|
3013
|
+
ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("Refreshing local cache from remote..."))}`);
|
|
3014
|
+
const refreshGlobalRes = await refreshLocalJournalFile(journalRepo, "global.md", join8(journalsDir, "global.md"));
|
|
2552
3015
|
if (!refreshGlobalRes.ok) {
|
|
2553
3016
|
if (!refreshGlobalRes.isNotFound) {
|
|
2554
|
-
ui.write(
|
|
3017
|
+
ui.write(import_picocolors10.default.red(`Failed to fetch global.md from ${journalRepo}:`));
|
|
2555
3018
|
ui.write(refreshGlobalRes.error);
|
|
2556
3019
|
process.exit(1);
|
|
2557
3020
|
}
|
|
2558
3021
|
}
|
|
2559
3022
|
const gotGlobal = refreshGlobalRes.ok && refreshGlobalRes.value;
|
|
2560
3023
|
if (gotGlobal) {
|
|
2561
|
-
ui.write(` ${
|
|
3024
|
+
ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("\u2713 global.md"))}`);
|
|
2562
3025
|
}
|
|
2563
3026
|
const refreshProjectCacheRes = await refreshLocalJournalFile(journalRepo, remoteProjectPath, localProjectPath);
|
|
2564
3027
|
if (!refreshProjectCacheRes.ok) {
|
|
2565
3028
|
if (!refreshProjectCacheRes.isNotFound) {
|
|
2566
|
-
ui.write(
|
|
3029
|
+
ui.write(import_picocolors10.default.red(`Failed to fetch ${remoteProjectPath} from ${journalRepo}:`));
|
|
2567
3030
|
ui.write(refreshProjectCacheRes.error);
|
|
2568
3031
|
process.exit(1);
|
|
2569
3032
|
}
|
|
2570
3033
|
}
|
|
2571
3034
|
const gotProjectCache = refreshProjectCacheRes.ok && refreshProjectCacheRes.value;
|
|
2572
3035
|
if (gotProjectCache) {
|
|
2573
|
-
ui.write(` ${
|
|
3036
|
+
ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray(`\u2713 ${remoteProjectPath}`))}`);
|
|
2574
3037
|
}
|
|
2575
|
-
const pendingFiles =
|
|
3038
|
+
const pendingFiles = existsSync9(pendingDir) ? readdirSync3(pendingDir).filter((f) => f.endsWith(".md") && f !== ".gitkeep").sort() : [];
|
|
2576
3039
|
if (pendingFiles.length === 0) {
|
|
2577
3040
|
ui.write(`
|
|
2578
|
-
${
|
|
3041
|
+
${import_picocolors10.default.yellow("\u26A0")} No pending entries. Local cache is now up to date.
|
|
2579
3042
|
`);
|
|
2580
3043
|
process.exit(0);
|
|
2581
3044
|
}
|
|
@@ -2588,7 +3051,7 @@ var init_sync = __esm(() => {
|
|
|
2588
3051
|
let currentFile = null;
|
|
2589
3052
|
if (!metaRes.ok) {
|
|
2590
3053
|
if (!metaRes.isNotFound) {
|
|
2591
|
-
ui.write(
|
|
3054
|
+
ui.write(import_picocolors10.default.red(`Failed to fetch ${remotePath} from ${journalRepo}:`));
|
|
2592
3055
|
ui.write(metaRes.error);
|
|
2593
3056
|
process.exit(1);
|
|
2594
3057
|
}
|
|
@@ -2599,14 +3062,14 @@ var init_sync = __esm(() => {
|
|
|
2599
3062
|
existingContent = Buffer.from(currentFile.content, "base64").toString("utf8");
|
|
2600
3063
|
currentSha = currentFile.sha;
|
|
2601
3064
|
if (args.verbose)
|
|
2602
|
-
ui.write(` ${
|
|
3065
|
+
ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("Found existing remote file (sha: " + (currentSha?.slice(0, 7) ?? "") + "...)"))}`);
|
|
2603
3066
|
} else {
|
|
2604
3067
|
if (args.verbose)
|
|
2605
|
-
ui.write(` ${
|
|
3068
|
+
ui.write(` ${import_picocolors10.default.dim(import_picocolors10.default.gray("No existing file on remote \u2014 will create it"))}`);
|
|
2606
3069
|
}
|
|
2607
3070
|
let newEntries = "";
|
|
2608
3071
|
for (const file of pendingFiles) {
|
|
2609
|
-
const fullPath =
|
|
3072
|
+
const fullPath = join8(pendingDir, file);
|
|
2610
3073
|
const entryContent = await Bun.file(fullPath).text();
|
|
2611
3074
|
newEntries += `
|
|
2612
3075
|
` + entryContent.trim() + `
|
|
@@ -2626,36 +3089,36 @@ var init_sync = __esm(() => {
|
|
|
2626
3089
|
const commitMessage = args.message || `journal: add ${pendingFiles.length} entr${pendingFiles.length === 1 ? "y" : "ies"} for ${project}`;
|
|
2627
3090
|
if (args.verbose)
|
|
2628
3091
|
ui.write(`
|
|
2629
|
-
${
|
|
3092
|
+
${import_picocolors10.default.dim(import_picocolors10.default.gray("Pushing to remote..."))}`);
|
|
2630
3093
|
try {
|
|
2631
3094
|
updateGitHubFile(journalRepo, remotePath, newContent, commitMessage, currentSha);
|
|
2632
|
-
ui.write(` ${
|
|
3095
|
+
ui.write(` ${import_picocolors10.default.green("\u2713")} ${import_picocolors10.default.white("Successfully pushed to")} ${import_picocolors10.default.white(remotePath)}`);
|
|
2633
3096
|
} catch (err) {
|
|
2634
|
-
ui.write(`${
|
|
3097
|
+
ui.write(`${import_picocolors10.default.red("\u2717")} ${import_picocolors10.default.white("Failed to push to GitHub.")}`);
|
|
2635
3098
|
process.exit(1);
|
|
2636
3099
|
}
|
|
2637
3100
|
for (const file of pendingFiles) {
|
|
2638
|
-
const fullPath =
|
|
3101
|
+
const fullPath = join8(pendingDir, file);
|
|
2639
3102
|
try {
|
|
2640
3103
|
await Bun.file(fullPath).unlink();
|
|
2641
3104
|
} catch {}
|
|
2642
3105
|
}
|
|
2643
|
-
ui.write(` ${
|
|
3106
|
+
ui.write(` ${import_picocolors10.default.green("\u2713")} ${import_picocolors10.default.white("Cleared local pending entries")}`);
|
|
2644
3107
|
try {
|
|
2645
3108
|
const refreshRes = await refreshLocalJournalFile(journalRepo, remotePath, localProjectPath);
|
|
2646
3109
|
if (!refreshRes.ok) {
|
|
2647
3110
|
if (!refreshRes.isNotFound) {
|
|
2648
|
-
ui.write(` ${
|
|
3111
|
+
ui.write(` ${import_picocolors10.default.yellow("\u26A0")} Could not re-fetch updated file (you can run sync again later)`);
|
|
2649
3112
|
}
|
|
2650
3113
|
} else if (refreshRes.value) {
|
|
2651
3114
|
if (args.verbose)
|
|
2652
|
-
ui.write(` ${
|
|
3115
|
+
ui.write(` ${import_picocolors10.default.green("\u2713")} ${import_picocolors10.default.white("Re-fetched")} ${import_picocolors10.default.white(project)}.md ${import_picocolors10.default.white("into local cache")}`);
|
|
2653
3116
|
}
|
|
2654
3117
|
} catch {
|
|
2655
|
-
ui.write(` ${
|
|
3118
|
+
ui.write(` ${import_picocolors10.default.yellow("\u26A0")} Could not re-fetch updated file (you can run sync again later)`);
|
|
2656
3119
|
}
|
|
2657
3120
|
ui.write(`
|
|
2658
|
-
${
|
|
3121
|
+
${import_picocolors10.default.green("Done!")} ${import_picocolors10.default.white(pendingFiles.length + " entr" + (pendingFiles.length === 1 ? "y" : "ies") + " published.")}
|
|
2659
3122
|
`);
|
|
2660
3123
|
process.exit(0);
|
|
2661
3124
|
}
|
|
@@ -2714,15 +3177,15 @@ var init_spec = __esm(() => {
|
|
|
2714
3177
|
});
|
|
2715
3178
|
|
|
2716
3179
|
// src/cli/commands/claude/context.ts
|
|
2717
|
-
import { existsSync as
|
|
2718
|
-
import { join as
|
|
3180
|
+
import { existsSync as existsSync10, readdirSync as readdirSync4 } from "fs";
|
|
3181
|
+
import { join as join9 } from "path";
|
|
2719
3182
|
function detectContext(cwd = process.cwd()) {
|
|
2720
3183
|
const claudeSpec = getProviderSpec("claude");
|
|
2721
|
-
const hasClaudeDir =
|
|
2722
|
-
const hasPluginManifest =
|
|
3184
|
+
const hasClaudeDir = existsSync10(join9(cwd, ".claude"));
|
|
3185
|
+
const hasPluginManifest = existsSync10(join9(cwd, claudeSpec.manifestPath));
|
|
2723
3186
|
let looseSkillFiles = [];
|
|
2724
3187
|
try {
|
|
2725
|
-
const files =
|
|
3188
|
+
const files = readdirSync4(cwd);
|
|
2726
3189
|
looseSkillFiles = files.filter((f) => {
|
|
2727
3190
|
if (!f.endsWith(".md") || f.startsWith("."))
|
|
2728
3191
|
return false;
|
|
@@ -2741,7 +3204,7 @@ function detectContext(cwd = process.cwd()) {
|
|
|
2741
3204
|
isEmpty
|
|
2742
3205
|
};
|
|
2743
3206
|
}
|
|
2744
|
-
var
|
|
3207
|
+
var init_context2 = __esm(() => {
|
|
2745
3208
|
init_spec();
|
|
2746
3209
|
});
|
|
2747
3210
|
|
|
@@ -2752,8 +3215,8 @@ __export(exports_new, {
|
|
|
2752
3215
|
default: () => new_default,
|
|
2753
3216
|
decidePath: () => decidePath
|
|
2754
3217
|
});
|
|
2755
|
-
import { join as
|
|
2756
|
-
import { mkdirSync as
|
|
3218
|
+
import { join as join10, basename as basename2, dirname as dirname2 } from "path";
|
|
3219
|
+
import { mkdirSync as mkdirSync3, writeFileSync, existsSync as existsSync11 } from "fs";
|
|
2757
3220
|
function decidePath(ctx, intent, providedName) {
|
|
2758
3221
|
const rawName = providedName || "";
|
|
2759
3222
|
let decisionPath = "standalone";
|
|
@@ -2767,7 +3230,7 @@ function decidePath(ctx, intent, providedName) {
|
|
|
2767
3230
|
targetDir = ctx.cwd;
|
|
2768
3231
|
shouldCreateDir = false;
|
|
2769
3232
|
} else {
|
|
2770
|
-
targetDir =
|
|
3233
|
+
targetDir = join10(ctx.cwd, rawName);
|
|
2771
3234
|
shouldCreateDir = true;
|
|
2772
3235
|
}
|
|
2773
3236
|
migrateExisting = ctx.looseSkillFiles.length > 0;
|
|
@@ -2777,7 +3240,7 @@ function decidePath(ctx, intent, providedName) {
|
|
|
2777
3240
|
targetDir = ctx.cwd;
|
|
2778
3241
|
shouldCreateDir = false;
|
|
2779
3242
|
} else {
|
|
2780
|
-
targetDir =
|
|
3243
|
+
targetDir = join10(ctx.cwd, rawName);
|
|
2781
3244
|
shouldCreateDir = true;
|
|
2782
3245
|
}
|
|
2783
3246
|
} else if (decisionPath === "standalone") {
|
|
@@ -2785,7 +3248,7 @@ function decidePath(ctx, intent, providedName) {
|
|
|
2785
3248
|
targetDir = ctx.cwd;
|
|
2786
3249
|
shouldCreateDir = false;
|
|
2787
3250
|
} else {
|
|
2788
|
-
targetDir =
|
|
3251
|
+
targetDir = join10(ctx.cwd, rawName);
|
|
2789
3252
|
shouldCreateDir = true;
|
|
2790
3253
|
}
|
|
2791
3254
|
}
|
|
@@ -2793,24 +3256,25 @@ function decidePath(ctx, intent, providedName) {
|
|
|
2793
3256
|
}
|
|
2794
3257
|
function scaffold(decision, ctx, migrateContent) {
|
|
2795
3258
|
const { targetDir, path, shouldCreateDir } = decision;
|
|
2796
|
-
if (
|
|
3259
|
+
if (existsSync11(targetDir) && shouldCreateDir) {
|
|
2797
3260
|
ui.fail("Target already exists");
|
|
2798
3261
|
process.exit(1);
|
|
2799
3262
|
}
|
|
2800
3263
|
if (shouldCreateDir) {
|
|
2801
|
-
|
|
3264
|
+
mkdirSync3(targetDir, { recursive: true });
|
|
2802
3265
|
}
|
|
2803
3266
|
if (path === "plugin") {
|
|
2804
3267
|
const pluginName = basename2(targetDir);
|
|
2805
3268
|
const claudeSpec = getProviderSpec("claude");
|
|
2806
|
-
const claudeManifestDir =
|
|
3269
|
+
const claudeManifestDir = dirname2(claudeSpec.manifestPath);
|
|
2807
3270
|
const pluginJson = {
|
|
2808
3271
|
name: pluginName,
|
|
2809
3272
|
description: "Scaffolded by doraval claude new",
|
|
2810
|
-
version: "0.1.0"
|
|
3273
|
+
version: "0.1.0",
|
|
3274
|
+
keywords: ["example-keyword", "another-keyword"]
|
|
2811
3275
|
};
|
|
2812
|
-
|
|
2813
|
-
writeFileSync(
|
|
3276
|
+
mkdirSync3(join10(targetDir, claudeManifestDir), { recursive: true });
|
|
3277
|
+
writeFileSync(join10(targetDir, claudeSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
|
|
2814
3278
|
const marketplaceJson = {
|
|
2815
3279
|
name: pluginName,
|
|
2816
3280
|
version: "0.1.0",
|
|
@@ -2821,9 +3285,9 @@ function scaffold(decision, ctx, migrateContent) {
|
|
|
2821
3285
|
license: "MIT",
|
|
2822
3286
|
keywords: ["claude-code", "skills", "plugin"]
|
|
2823
3287
|
};
|
|
2824
|
-
writeFileSync(
|
|
3288
|
+
writeFileSync(join10(targetDir, "marketplace.json"), JSON.stringify(marketplaceJson, null, 2));
|
|
2825
3289
|
const demoSkillName = "doraval";
|
|
2826
|
-
|
|
3290
|
+
mkdirSync3(join10(targetDir, "skills", demoSkillName), { recursive: true });
|
|
2827
3291
|
let skillContent;
|
|
2828
3292
|
if (migrateContent) {
|
|
2829
3293
|
skillContent = migrateContent;
|
|
@@ -2847,19 +3311,19 @@ When you need to check a skill or plugin:
|
|
|
2847
3311
|
|
|
2848
3312
|
Always run \`doraval validate\` before sharing or publishing a plugin. This skill demonstrates a complete, self-referential example of using doraval inside a generated plugin.`;
|
|
2849
3313
|
}
|
|
2850
|
-
writeFileSync(
|
|
2851
|
-
const readmePath =
|
|
2852
|
-
if (!
|
|
3314
|
+
writeFileSync(join10(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
|
|
3315
|
+
const readmePath = join10(targetDir, "README.md");
|
|
3316
|
+
if (!existsSync11(readmePath)) {
|
|
2853
3317
|
writeFileSync(readmePath, "# " + pluginName + `
|
|
2854
3318
|
|
|
2855
3319
|
Claude Code plugin scaffolded by doraval.`);
|
|
2856
3320
|
}
|
|
2857
3321
|
} else {
|
|
2858
|
-
|
|
3322
|
+
mkdirSync3(join10(targetDir, ".claude", "skills", "my-skill"), { recursive: true });
|
|
2859
3323
|
const skillBody = migrateContent || `# My Skill
|
|
2860
3324
|
|
|
2861
3325
|
Basic starter.`;
|
|
2862
|
-
writeFileSync(
|
|
3326
|
+
writeFileSync(join10(targetDir, ".claude", "skills", "my-skill", "SKILL.md"), `---
|
|
2863
3327
|
name: my-skill
|
|
2864
3328
|
description: Starter
|
|
2865
3329
|
---
|
|
@@ -2867,14 +3331,14 @@ description: Starter
|
|
|
2867
3331
|
${skillBody}`);
|
|
2868
3332
|
}
|
|
2869
3333
|
}
|
|
2870
|
-
var
|
|
3334
|
+
var import_picocolors11, new_default;
|
|
2871
3335
|
var init_new = __esm(() => {
|
|
2872
3336
|
init_dist();
|
|
2873
3337
|
init_out();
|
|
2874
|
-
|
|
3338
|
+
init_context2();
|
|
2875
3339
|
init_prompt();
|
|
2876
3340
|
init_spec();
|
|
2877
|
-
|
|
3341
|
+
import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
2878
3342
|
new_default = defineCommand({
|
|
2879
3343
|
meta: {
|
|
2880
3344
|
name: "new",
|
|
@@ -2913,7 +3377,7 @@ var init_new = __esm(() => {
|
|
|
2913
3377
|
}
|
|
2914
3378
|
scaffold(decision, ctx, migrateContent);
|
|
2915
3379
|
ui.write(`
|
|
2916
|
-
${
|
|
3380
|
+
${import_picocolors11.default.green("\u2713")} Created ${decision.path} at ${import_picocolors11.default.bold(decision.targetDir)}`);
|
|
2917
3381
|
const cmdName = decision.path === "plugin" ? `/${basename2(decision.targetDir)}:doraval` : "/my-skill";
|
|
2918
3382
|
ui.info(` Command: ${cmdName}`);
|
|
2919
3383
|
if (decision.path === "plugin") {
|
|
@@ -2923,6 +3387,9 @@ var init_new = __esm(() => {
|
|
|
2923
3387
|
}
|
|
2924
3388
|
ui.info(` Test: claude --plugin-dir ${decision.targetDir} (or use normally for standalone)`);
|
|
2925
3389
|
ui.info(` Validate: doraval validate ${decision.targetDir}`);
|
|
3390
|
+
if (decision.path === "plugin") {
|
|
3391
|
+
ui.info(` Keywords: keywords array added for discovery \u2014 run validate to see "If users mention any of these keywords, your plugin will get triggered"`);
|
|
3392
|
+
}
|
|
2926
3393
|
if (decision.path === "plugin" && decision.migrateExisting) {
|
|
2927
3394
|
ui.info(" (Existing content migrated where confirmed.)");
|
|
2928
3395
|
}
|
|
@@ -2936,8 +3403,8 @@ var exports_bump = {};
|
|
|
2936
3403
|
__export(exports_bump, {
|
|
2937
3404
|
default: () => bump_default
|
|
2938
3405
|
});
|
|
2939
|
-
import { resolve as resolve4, join as
|
|
2940
|
-
import { existsSync as
|
|
3406
|
+
import { resolve as resolve4, join as join11, dirname as dirname3, relative } from "path";
|
|
3407
|
+
import { existsSync as existsSync12, readFileSync, writeFileSync as writeFileSync2, readdirSync as readdirSync5, statSync } from "fs";
|
|
2941
3408
|
function bumpVersion(current, type) {
|
|
2942
3409
|
if (/^\d+\.\d+\.\d+$/.test(type))
|
|
2943
3410
|
return type;
|
|
@@ -2955,7 +3422,7 @@ function bumpVersion(current, type) {
|
|
|
2955
3422
|
throw new Error(`Invalid bump type "${type}". Use patch, minor, major, or an exact version like 1.2.3`);
|
|
2956
3423
|
}
|
|
2957
3424
|
}
|
|
2958
|
-
function
|
|
3425
|
+
function readJson2(p) {
|
|
2959
3426
|
try {
|
|
2960
3427
|
const content = readFileSync(p, "utf8");
|
|
2961
3428
|
return JSON.parse(content);
|
|
@@ -2963,7 +3430,7 @@ function readJson(p) {
|
|
|
2963
3430
|
return null;
|
|
2964
3431
|
}
|
|
2965
3432
|
}
|
|
2966
|
-
function
|
|
3433
|
+
function writeJson2(p, data) {
|
|
2967
3434
|
writeFileSync2(p, JSON.stringify(data, null, 2) + `
|
|
2968
3435
|
`, "utf8");
|
|
2969
3436
|
}
|
|
@@ -3015,12 +3482,12 @@ function walkForTargets(dir, maxDepth = 6, currentDepth = 0) {
|
|
|
3015
3482
|
return results;
|
|
3016
3483
|
let entries;
|
|
3017
3484
|
try {
|
|
3018
|
-
entries =
|
|
3485
|
+
entries = readdirSync5(dir);
|
|
3019
3486
|
} catch {
|
|
3020
3487
|
return results;
|
|
3021
3488
|
}
|
|
3022
3489
|
for (const entry of entries) {
|
|
3023
|
-
const full =
|
|
3490
|
+
const full = join11(dir, entry);
|
|
3024
3491
|
let st;
|
|
3025
3492
|
try {
|
|
3026
3493
|
st = statSync(full);
|
|
@@ -3032,7 +3499,7 @@ function walkForTargets(dir, maxDepth = 6, currentDepth = 0) {
|
|
|
3032
3499
|
results.push(...sub);
|
|
3033
3500
|
} else if (st.isFile()) {
|
|
3034
3501
|
if (entry === "plugin.json") {
|
|
3035
|
-
const parentDir =
|
|
3502
|
+
const parentDir = dirname3(full);
|
|
3036
3503
|
const parentName = parentDir.split(/[/\\]/).pop();
|
|
3037
3504
|
if (parentName === ".claude-plugin" || parentName === ".codex-plugin" || parentName === ".cursor-plugin" || parentName === ".github") {
|
|
3038
3505
|
results.push({
|
|
@@ -3042,7 +3509,7 @@ function walkForTargets(dir, maxDepth = 6, currentDepth = 0) {
|
|
|
3042
3509
|
});
|
|
3043
3510
|
}
|
|
3044
3511
|
} else if (entry === "marketplace.json") {
|
|
3045
|
-
const json =
|
|
3512
|
+
const json = readJson2(full);
|
|
3046
3513
|
if (json && getVersion(json)) {
|
|
3047
3514
|
results.push({
|
|
3048
3515
|
file: full,
|
|
@@ -3055,11 +3522,11 @@ function walkForTargets(dir, maxDepth = 6, currentDepth = 0) {
|
|
|
3055
3522
|
}
|
|
3056
3523
|
return results;
|
|
3057
3524
|
}
|
|
3058
|
-
var
|
|
3525
|
+
var import_picocolors12, bump_default;
|
|
3059
3526
|
var init_bump = __esm(() => {
|
|
3060
3527
|
init_dist();
|
|
3061
3528
|
init_out();
|
|
3062
|
-
|
|
3529
|
+
import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
3063
3530
|
bump_default = defineCommand({
|
|
3064
3531
|
meta: {
|
|
3065
3532
|
name: "bump",
|
|
@@ -3093,7 +3560,7 @@ var init_bump = __esm(() => {
|
|
|
3093
3560
|
}
|
|
3094
3561
|
const isKnownType = ["patch", "minor", "major"].includes(rawType) || /^\d+\.\d+\.\d+$/.test(rawType);
|
|
3095
3562
|
const maybePath = resolve4(rawType);
|
|
3096
|
-
const looksLikeDir =
|
|
3563
|
+
const looksLikeDir = existsSync12(maybePath) || rawType === "." || rawType.startsWith("./") || rawType.startsWith("../");
|
|
3097
3564
|
if (!isKnownType && looksLikeDir) {
|
|
3098
3565
|
targetPath = rawType;
|
|
3099
3566
|
rawType = "patch";
|
|
@@ -3102,7 +3569,7 @@ var init_bump = __esm(() => {
|
|
|
3102
3569
|
process.exit(1);
|
|
3103
3570
|
}
|
|
3104
3571
|
const root = resolve4(targetPath);
|
|
3105
|
-
if (!
|
|
3572
|
+
if (!existsSync12(root)) {
|
|
3106
3573
|
ui.fail(`Path does not exist: ${root}`);
|
|
3107
3574
|
process.exit(1);
|
|
3108
3575
|
}
|
|
@@ -3137,7 +3604,7 @@ var init_bump = __esm(() => {
|
|
|
3137
3604
|
ui.info(` matched ${targets.length} file(s)`);
|
|
3138
3605
|
let bumpedCount = 0;
|
|
3139
3606
|
for (const t of targets) {
|
|
3140
|
-
const json =
|
|
3607
|
+
const json = readJson2(t.file);
|
|
3141
3608
|
if (!json || typeof json !== "object") {
|
|
3142
3609
|
ui.warnItem(`skipped (invalid JSON): ${relative(root, t.file)}`);
|
|
3143
3610
|
continue;
|
|
@@ -3166,11 +3633,11 @@ var init_bump = __esm(() => {
|
|
|
3166
3633
|
ui.warnItem(`skipped (could not locate version field to update): ${relPath}`);
|
|
3167
3634
|
continue;
|
|
3168
3635
|
}
|
|
3169
|
-
|
|
3636
|
+
writeJson2(t.file, json);
|
|
3170
3637
|
if (didRootUpdate && current) {
|
|
3171
|
-
ui.success(`${t.label}: ${
|
|
3638
|
+
ui.success(`${t.label}: ${import_picocolors12.default.dim(current)} \u2192 ${import_picocolors12.default.green(next)}`);
|
|
3172
3639
|
} else if (didRootUpdate) {
|
|
3173
|
-
ui.success(`${t.label}: ${
|
|
3640
|
+
ui.success(`${t.label}: ${import_picocolors12.default.green(next)}`);
|
|
3174
3641
|
} else {
|
|
3175
3642
|
ui.success(`${t.label} (no root version)`);
|
|
3176
3643
|
}
|
|
@@ -3193,16 +3660,16 @@ var init_bump = __esm(() => {
|
|
|
3193
3660
|
});
|
|
3194
3661
|
|
|
3195
3662
|
// src/cli/commands/codex/context.ts
|
|
3196
|
-
import { existsSync as
|
|
3197
|
-
import { join as
|
|
3663
|
+
import { existsSync as existsSync13, readdirSync as readdirSync6 } from "fs";
|
|
3664
|
+
import { join as join12 } from "path";
|
|
3198
3665
|
function detectContext2(cwd = process.cwd()) {
|
|
3199
3666
|
const codexSpec = getProviderSpec("codex");
|
|
3200
|
-
const hasCodexDir =
|
|
3201
|
-
const hasPluginManifest =
|
|
3202
|
-
const hasMarketplace =
|
|
3667
|
+
const hasCodexDir = existsSync13(join12(cwd, ".codex"));
|
|
3668
|
+
const hasPluginManifest = existsSync13(join12(cwd, codexSpec.manifestPath));
|
|
3669
|
+
const hasMarketplace = existsSync13(join12(cwd, ".agents", "plugins", "marketplace.json")) || existsSync13(join12(cwd, codexSpec.manifestPath));
|
|
3203
3670
|
let looseSkillFiles = [];
|
|
3204
3671
|
try {
|
|
3205
|
-
const files =
|
|
3672
|
+
const files = readdirSync6(cwd);
|
|
3206
3673
|
looseSkillFiles = files.filter((f) => {
|
|
3207
3674
|
if (!f.endsWith(".md") || f.startsWith("."))
|
|
3208
3675
|
return false;
|
|
@@ -3222,7 +3689,7 @@ function detectContext2(cwd = process.cwd()) {
|
|
|
3222
3689
|
isEmpty
|
|
3223
3690
|
};
|
|
3224
3691
|
}
|
|
3225
|
-
var
|
|
3692
|
+
var init_context3 = __esm(() => {
|
|
3226
3693
|
init_spec();
|
|
3227
3694
|
});
|
|
3228
3695
|
|
|
@@ -3233,8 +3700,8 @@ __export(exports_new2, {
|
|
|
3233
3700
|
default: () => new_default2,
|
|
3234
3701
|
decidePath: () => decidePath2
|
|
3235
3702
|
});
|
|
3236
|
-
import { join as
|
|
3237
|
-
import { mkdirSync as
|
|
3703
|
+
import { join as join13, basename as basename3, dirname as dirname4 } from "path";
|
|
3704
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, existsSync as existsSync14 } from "fs";
|
|
3238
3705
|
function decidePath2(ctx, intent, providedName) {
|
|
3239
3706
|
const rawName = providedName || "";
|
|
3240
3707
|
let decisionPath = "standalone";
|
|
@@ -3248,7 +3715,7 @@ function decidePath2(ctx, intent, providedName) {
|
|
|
3248
3715
|
targetDir = ctx.cwd;
|
|
3249
3716
|
shouldCreateDir = false;
|
|
3250
3717
|
} else {
|
|
3251
|
-
targetDir =
|
|
3718
|
+
targetDir = join13(ctx.cwd, rawName);
|
|
3252
3719
|
shouldCreateDir = true;
|
|
3253
3720
|
}
|
|
3254
3721
|
migrateExisting = ctx.looseSkillFiles.length > 0;
|
|
@@ -3258,7 +3725,7 @@ function decidePath2(ctx, intent, providedName) {
|
|
|
3258
3725
|
targetDir = ctx.cwd;
|
|
3259
3726
|
shouldCreateDir = false;
|
|
3260
3727
|
} else {
|
|
3261
|
-
targetDir =
|
|
3728
|
+
targetDir = join13(ctx.cwd, rawName);
|
|
3262
3729
|
shouldCreateDir = true;
|
|
3263
3730
|
}
|
|
3264
3731
|
} else if (decisionPath === "standalone") {
|
|
@@ -3266,7 +3733,7 @@ function decidePath2(ctx, intent, providedName) {
|
|
|
3266
3733
|
targetDir = ctx.cwd;
|
|
3267
3734
|
shouldCreateDir = false;
|
|
3268
3735
|
} else {
|
|
3269
|
-
targetDir =
|
|
3736
|
+
targetDir = join13(ctx.cwd, rawName);
|
|
3270
3737
|
shouldCreateDir = true;
|
|
3271
3738
|
}
|
|
3272
3739
|
}
|
|
@@ -3274,17 +3741,17 @@ function decidePath2(ctx, intent, providedName) {
|
|
|
3274
3741
|
}
|
|
3275
3742
|
function scaffold2(decision, ctx, migrateContent) {
|
|
3276
3743
|
const { targetDir, path, shouldCreateDir } = decision;
|
|
3277
|
-
if (
|
|
3744
|
+
if (existsSync14(targetDir) && shouldCreateDir) {
|
|
3278
3745
|
ui.fail("Target already exists");
|
|
3279
3746
|
process.exit(1);
|
|
3280
3747
|
}
|
|
3281
3748
|
if (shouldCreateDir) {
|
|
3282
|
-
|
|
3749
|
+
mkdirSync4(targetDir, { recursive: true });
|
|
3283
3750
|
}
|
|
3284
3751
|
if (path === "plugin") {
|
|
3285
3752
|
const pluginName = basename3(targetDir);
|
|
3286
3753
|
const codexSpec = getProviderSpec("codex");
|
|
3287
|
-
const codexManifestDir =
|
|
3754
|
+
const codexManifestDir = dirname4(codexSpec.manifestPath);
|
|
3288
3755
|
const pluginJson = {
|
|
3289
3756
|
name: pluginName,
|
|
3290
3757
|
version: "0.1.0",
|
|
@@ -3294,12 +3761,13 @@ function scaffold2(decision, ctx, migrateContent) {
|
|
|
3294
3761
|
displayName: pluginName,
|
|
3295
3762
|
shortDescription: "Scaffolded starter plugin",
|
|
3296
3763
|
category: "Productivity"
|
|
3297
|
-
}
|
|
3764
|
+
},
|
|
3765
|
+
keywords: ["example-keyword", "another-keyword"]
|
|
3298
3766
|
};
|
|
3299
|
-
|
|
3300
|
-
writeFileSync3(
|
|
3301
|
-
const marketplaceDir =
|
|
3302
|
-
|
|
3767
|
+
mkdirSync4(join13(targetDir, codexManifestDir), { recursive: true });
|
|
3768
|
+
writeFileSync3(join13(targetDir, codexSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
|
|
3769
|
+
const marketplaceDir = dirname4(codexSpec.marketplacePath);
|
|
3770
|
+
mkdirSync4(join13(targetDir, marketplaceDir), { recursive: true });
|
|
3303
3771
|
const marketplaceJson = {
|
|
3304
3772
|
name: "local",
|
|
3305
3773
|
interface: {
|
|
@@ -3320,9 +3788,9 @@ function scaffold2(decision, ctx, migrateContent) {
|
|
|
3320
3788
|
}
|
|
3321
3789
|
]
|
|
3322
3790
|
};
|
|
3323
|
-
writeFileSync3(
|
|
3791
|
+
writeFileSync3(join13(targetDir, codexSpec.marketplacePath), JSON.stringify(marketplaceJson, null, 2));
|
|
3324
3792
|
const demoSkillName = "doraval";
|
|
3325
|
-
|
|
3793
|
+
mkdirSync4(join13(targetDir, "skills", demoSkillName), { recursive: true });
|
|
3326
3794
|
let skillContent;
|
|
3327
3795
|
if (migrateContent) {
|
|
3328
3796
|
skillContent = migrateContent;
|
|
@@ -3353,19 +3821,19 @@ To test in Codex:
|
|
|
3353
3821
|
3. Open the plugin directory, select your local marketplace, and enable the plugin.
|
|
3354
3822
|
4. Invoke the demo with /${pluginName}:doraval`;
|
|
3355
3823
|
}
|
|
3356
|
-
writeFileSync3(
|
|
3357
|
-
const readmePath =
|
|
3358
|
-
if (!
|
|
3824
|
+
writeFileSync3(join13(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
|
|
3825
|
+
const readmePath = join13(targetDir, "README.md");
|
|
3826
|
+
if (!existsSync14(readmePath)) {
|
|
3359
3827
|
writeFileSync3(readmePath, "# " + pluginName + `
|
|
3360
3828
|
|
|
3361
3829
|
Codex plugin scaffolded by doraval.`);
|
|
3362
3830
|
}
|
|
3363
3831
|
} else {
|
|
3364
|
-
|
|
3832
|
+
mkdirSync4(join13(targetDir, "skills", "doraval"), { recursive: true });
|
|
3365
3833
|
const skillBody = migrateContent || `# My Skill
|
|
3366
3834
|
|
|
3367
3835
|
Basic starter for Codex.`;
|
|
3368
|
-
writeFileSync3(
|
|
3836
|
+
writeFileSync3(join13(targetDir, "skills", "doraval", "SKILL.md"), `---
|
|
3369
3837
|
name: doraval
|
|
3370
3838
|
description: Starter (local skill)
|
|
3371
3839
|
---
|
|
@@ -3373,14 +3841,14 @@ description: Starter (local skill)
|
|
|
3373
3841
|
${skillBody}`);
|
|
3374
3842
|
}
|
|
3375
3843
|
}
|
|
3376
|
-
var
|
|
3844
|
+
var import_picocolors13, new_default2;
|
|
3377
3845
|
var init_new2 = __esm(() => {
|
|
3378
3846
|
init_dist();
|
|
3379
3847
|
init_out();
|
|
3380
|
-
|
|
3848
|
+
init_context3();
|
|
3381
3849
|
init_prompt();
|
|
3382
3850
|
init_spec();
|
|
3383
|
-
|
|
3851
|
+
import_picocolors13 = __toESM(require_picocolors(), 1);
|
|
3384
3852
|
new_default2 = defineCommand({
|
|
3385
3853
|
meta: {
|
|
3386
3854
|
name: "new",
|
|
@@ -3419,7 +3887,7 @@ var init_new2 = __esm(() => {
|
|
|
3419
3887
|
}
|
|
3420
3888
|
scaffold2(decision, ctx, migrateContent);
|
|
3421
3889
|
ui.write(`
|
|
3422
|
-
${
|
|
3890
|
+
${import_picocolors13.default.green("\u2713")} Created ${decision.path} at ${import_picocolors13.default.bold(decision.targetDir)}`);
|
|
3423
3891
|
const cmdName = decision.path === "plugin" ? `/${basename3(decision.targetDir)}:doraval` : "/doraval (local skill)";
|
|
3424
3892
|
ui.info(` Command: ${cmdName}`);
|
|
3425
3893
|
if (decision.path === "plugin") {
|
|
@@ -3429,6 +3897,9 @@ var init_new2 = __esm(() => {
|
|
|
3429
3897
|
}
|
|
3430
3898
|
ui.info(` Test (local): restart Codex, select your marketplace in the plugin directory`);
|
|
3431
3899
|
ui.info(` Validate: doraval validate ${decision.targetDir}`);
|
|
3900
|
+
if (decision.path === "plugin") {
|
|
3901
|
+
ui.info(` Keywords: keywords array added for discovery \u2014 run validate to see "If users mention any of these keywords, your plugin will get triggered"`);
|
|
3902
|
+
}
|
|
3432
3903
|
if (decision.path === "plugin" && decision.migrateExisting) {
|
|
3433
3904
|
ui.info(" (Existing content migrated where confirmed.)");
|
|
3434
3905
|
}
|
|
@@ -3438,14 +3909,14 @@ var init_new2 = __esm(() => {
|
|
|
3438
3909
|
});
|
|
3439
3910
|
|
|
3440
3911
|
// src/cli/commands/cursor/context.ts
|
|
3441
|
-
import { existsSync as
|
|
3442
|
-
import { join as
|
|
3912
|
+
import { existsSync as existsSync15, readdirSync as readdirSync7 } from "fs";
|
|
3913
|
+
import { join as join14 } from "path";
|
|
3443
3914
|
function detectContext3(cwd = process.cwd()) {
|
|
3444
|
-
const hasCursorDir =
|
|
3445
|
-
const hasPluginManifest =
|
|
3915
|
+
const hasCursorDir = existsSync15(join14(cwd, ".cursor"));
|
|
3916
|
+
const hasPluginManifest = existsSync15(join14(cwd, ".cursor-plugin", "plugin.json"));
|
|
3446
3917
|
let looseSkillFiles = [];
|
|
3447
3918
|
try {
|
|
3448
|
-
const files =
|
|
3919
|
+
const files = readdirSync7(cwd);
|
|
3449
3920
|
looseSkillFiles = files.filter((f) => {
|
|
3450
3921
|
if (!f.endsWith(".md") || f.startsWith("."))
|
|
3451
3922
|
return false;
|
|
@@ -3464,7 +3935,7 @@ function detectContext3(cwd = process.cwd()) {
|
|
|
3464
3935
|
isEmpty
|
|
3465
3936
|
};
|
|
3466
3937
|
}
|
|
3467
|
-
var
|
|
3938
|
+
var init_context4 = () => {};
|
|
3468
3939
|
|
|
3469
3940
|
// src/cli/commands/cursor/new.ts
|
|
3470
3941
|
var exports_new3 = {};
|
|
@@ -3473,8 +3944,8 @@ __export(exports_new3, {
|
|
|
3473
3944
|
default: () => new_default3,
|
|
3474
3945
|
decidePath: () => decidePath3
|
|
3475
3946
|
});
|
|
3476
|
-
import { join as
|
|
3477
|
-
import { mkdirSync as
|
|
3947
|
+
import { join as join15, basename as basename4, dirname as dirname5 } from "path";
|
|
3948
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4, existsSync as existsSync16 } from "fs";
|
|
3478
3949
|
function decidePath3(ctx, intent, providedName) {
|
|
3479
3950
|
const rawName = providedName || "";
|
|
3480
3951
|
let decisionPath = "standalone";
|
|
@@ -3488,7 +3959,7 @@ function decidePath3(ctx, intent, providedName) {
|
|
|
3488
3959
|
targetDir = ctx.cwd;
|
|
3489
3960
|
shouldCreateDir = false;
|
|
3490
3961
|
} else {
|
|
3491
|
-
targetDir =
|
|
3962
|
+
targetDir = join15(ctx.cwd, rawName);
|
|
3492
3963
|
shouldCreateDir = true;
|
|
3493
3964
|
}
|
|
3494
3965
|
migrateExisting = ctx.looseSkillFiles.length > 0;
|
|
@@ -3498,7 +3969,7 @@ function decidePath3(ctx, intent, providedName) {
|
|
|
3498
3969
|
targetDir = ctx.cwd;
|
|
3499
3970
|
shouldCreateDir = false;
|
|
3500
3971
|
} else {
|
|
3501
|
-
targetDir =
|
|
3972
|
+
targetDir = join15(ctx.cwd, rawName);
|
|
3502
3973
|
shouldCreateDir = true;
|
|
3503
3974
|
}
|
|
3504
3975
|
} else if (decisionPath === "standalone") {
|
|
@@ -3506,7 +3977,7 @@ function decidePath3(ctx, intent, providedName) {
|
|
|
3506
3977
|
targetDir = ctx.cwd;
|
|
3507
3978
|
shouldCreateDir = false;
|
|
3508
3979
|
} else {
|
|
3509
|
-
targetDir =
|
|
3980
|
+
targetDir = join15(ctx.cwd, rawName);
|
|
3510
3981
|
shouldCreateDir = true;
|
|
3511
3982
|
}
|
|
3512
3983
|
}
|
|
@@ -3514,28 +3985,29 @@ function decidePath3(ctx, intent, providedName) {
|
|
|
3514
3985
|
}
|
|
3515
3986
|
function scaffold3(decision, ctx, migrateContent) {
|
|
3516
3987
|
const { targetDir, path, shouldCreateDir } = decision;
|
|
3517
|
-
if (
|
|
3988
|
+
if (existsSync16(targetDir) && shouldCreateDir) {
|
|
3518
3989
|
ui.fail("Target already exists");
|
|
3519
3990
|
process.exit(1);
|
|
3520
3991
|
}
|
|
3521
3992
|
if (shouldCreateDir) {
|
|
3522
|
-
|
|
3993
|
+
mkdirSync5(targetDir, { recursive: true });
|
|
3523
3994
|
}
|
|
3524
3995
|
if (path === "plugin") {
|
|
3525
3996
|
const pluginName = basename4(targetDir);
|
|
3526
3997
|
const cursorSpec = getProviderSpec("cursor");
|
|
3527
|
-
const cursorManifestDir =
|
|
3998
|
+
const cursorManifestDir = dirname5(cursorSpec.manifestPath);
|
|
3528
3999
|
const pluginJson = {
|
|
3529
4000
|
name: pluginName,
|
|
3530
4001
|
version: "0.1.0",
|
|
3531
4002
|
description: "Scaffolded by doraval cursor new",
|
|
3532
4003
|
skills: "./skills/",
|
|
3533
|
-
displayName: pluginName
|
|
4004
|
+
displayName: pluginName,
|
|
4005
|
+
keywords: ["example-keyword", "another-keyword"]
|
|
3534
4006
|
};
|
|
3535
|
-
|
|
3536
|
-
writeFileSync4(
|
|
3537
|
-
const marketplaceDir =
|
|
3538
|
-
|
|
4007
|
+
mkdirSync5(join15(targetDir, cursorManifestDir), { recursive: true });
|
|
4008
|
+
writeFileSync4(join15(targetDir, cursorSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
|
|
4009
|
+
const marketplaceDir = dirname5(cursorSpec.marketplacePath);
|
|
4010
|
+
mkdirSync5(join15(targetDir, marketplaceDir), { recursive: true });
|
|
3539
4011
|
const marketplaceJson = {
|
|
3540
4012
|
name: pluginName,
|
|
3541
4013
|
version: "0.1.0",
|
|
@@ -3546,9 +4018,9 @@ function scaffold3(decision, ctx, migrateContent) {
|
|
|
3546
4018
|
license: "MIT",
|
|
3547
4019
|
keywords: ["cursor", "skills", "plugin"]
|
|
3548
4020
|
};
|
|
3549
|
-
writeFileSync4(
|
|
4021
|
+
writeFileSync4(join15(targetDir, cursorSpec.marketplacePath), JSON.stringify(marketplaceJson, null, 2));
|
|
3550
4022
|
const demoSkillName = "doraval";
|
|
3551
|
-
|
|
4023
|
+
mkdirSync5(join15(targetDir, "skills", demoSkillName), { recursive: true });
|
|
3552
4024
|
let skillContent;
|
|
3553
4025
|
if (migrateContent) {
|
|
3554
4026
|
skillContent = migrateContent;
|
|
@@ -3577,19 +4049,19 @@ To test in Cursor:
|
|
|
3577
4049
|
1. Open the plugin directory or add via marketplace.
|
|
3578
4050
|
2. The demo skill will be available.`;
|
|
3579
4051
|
}
|
|
3580
|
-
writeFileSync4(
|
|
3581
|
-
const readmePath =
|
|
3582
|
-
if (!
|
|
4052
|
+
writeFileSync4(join15(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
|
|
4053
|
+
const readmePath = join15(targetDir, "README.md");
|
|
4054
|
+
if (!existsSync16(readmePath)) {
|
|
3583
4055
|
writeFileSync4(readmePath, "# " + pluginName + `
|
|
3584
4056
|
|
|
3585
4057
|
Cursor plugin scaffolded by doraval.`);
|
|
3586
4058
|
}
|
|
3587
4059
|
} else {
|
|
3588
|
-
|
|
4060
|
+
mkdirSync5(join15(targetDir, "skills", "doraval"), { recursive: true });
|
|
3589
4061
|
const skillBody = migrateContent || `# My Skill
|
|
3590
4062
|
|
|
3591
4063
|
Basic starter for Cursor.`;
|
|
3592
|
-
writeFileSync4(
|
|
4064
|
+
writeFileSync4(join15(targetDir, "skills", "doraval", "SKILL.md"), `---
|
|
3593
4065
|
name: doraval
|
|
3594
4066
|
description: Starter (local skill)
|
|
3595
4067
|
---
|
|
@@ -3597,14 +4069,14 @@ description: Starter (local skill)
|
|
|
3597
4069
|
${skillBody}`);
|
|
3598
4070
|
}
|
|
3599
4071
|
}
|
|
3600
|
-
var
|
|
4072
|
+
var import_picocolors14, new_default3;
|
|
3601
4073
|
var init_new3 = __esm(() => {
|
|
3602
4074
|
init_dist();
|
|
3603
4075
|
init_out();
|
|
3604
|
-
|
|
4076
|
+
init_context4();
|
|
3605
4077
|
init_prompt();
|
|
3606
4078
|
init_spec();
|
|
3607
|
-
|
|
4079
|
+
import_picocolors14 = __toESM(require_picocolors(), 1);
|
|
3608
4080
|
new_default3 = defineCommand({
|
|
3609
4081
|
meta: {
|
|
3610
4082
|
name: "new",
|
|
@@ -3643,7 +4115,7 @@ var init_new3 = __esm(() => {
|
|
|
3643
4115
|
}
|
|
3644
4116
|
scaffold3(decision, ctx, migrateContent);
|
|
3645
4117
|
ui.write(`
|
|
3646
|
-
${
|
|
4118
|
+
${import_picocolors14.default.green("\u2713")} Created ${decision.path} at ${import_picocolors14.default.bold(decision.targetDir)}`);
|
|
3647
4119
|
const cmdName = decision.path === "plugin" ? `/${basename4(decision.targetDir)}:doraval` : "/doraval (local skill)";
|
|
3648
4120
|
ui.info(` Command: ${cmdName}`);
|
|
3649
4121
|
if (decision.path === "plugin") {
|
|
@@ -3652,6 +4124,9 @@ var init_new3 = __esm(() => {
|
|
|
3652
4124
|
}
|
|
3653
4125
|
ui.info(` Test (local): add the plugin dir in Cursor settings or use local skills`);
|
|
3654
4126
|
ui.info(` Validate: doraval validate ${decision.targetDir}`);
|
|
4127
|
+
if (decision.path === "plugin") {
|
|
4128
|
+
ui.info(` Keywords: keywords array added for discovery \u2014 run validate to see "If users mention any of these keywords, your plugin will get triggered"`);
|
|
4129
|
+
}
|
|
3655
4130
|
if (decision.path === "plugin" && decision.migrateExisting) {
|
|
3656
4131
|
ui.info(" (Existing content migrated where confirmed.)");
|
|
3657
4132
|
}
|
|
@@ -3661,14 +4136,14 @@ var init_new3 = __esm(() => {
|
|
|
3661
4136
|
});
|
|
3662
4137
|
|
|
3663
4138
|
// src/cli/commands/copilot/context.ts
|
|
3664
|
-
import { existsSync as
|
|
3665
|
-
import { join as
|
|
4139
|
+
import { existsSync as existsSync17, readdirSync as readdirSync8 } from "fs";
|
|
4140
|
+
import { join as join16 } from "path";
|
|
3666
4141
|
function detectContext4(cwd = process.cwd()) {
|
|
3667
|
-
const hasGithubDir =
|
|
3668
|
-
const hasPluginManifest =
|
|
4142
|
+
const hasGithubDir = existsSync17(join16(cwd, ".github"));
|
|
4143
|
+
const hasPluginManifest = existsSync17(join16(cwd, ".github", "plugin", "plugin.json"));
|
|
3669
4144
|
let looseSkillFiles = [];
|
|
3670
4145
|
try {
|
|
3671
|
-
const files =
|
|
4146
|
+
const files = readdirSync8(cwd);
|
|
3672
4147
|
looseSkillFiles = files.filter((f) => {
|
|
3673
4148
|
if (!f.endsWith(".md") || f.startsWith("."))
|
|
3674
4149
|
return false;
|
|
@@ -3687,7 +4162,7 @@ function detectContext4(cwd = process.cwd()) {
|
|
|
3687
4162
|
isEmpty
|
|
3688
4163
|
};
|
|
3689
4164
|
}
|
|
3690
|
-
var
|
|
4165
|
+
var init_context5 = () => {};
|
|
3691
4166
|
|
|
3692
4167
|
// src/cli/commands/copilot/new.ts
|
|
3693
4168
|
var exports_new4 = {};
|
|
@@ -3696,8 +4171,8 @@ __export(exports_new4, {
|
|
|
3696
4171
|
default: () => new_default4,
|
|
3697
4172
|
decidePath: () => decidePath4
|
|
3698
4173
|
});
|
|
3699
|
-
import { join as
|
|
3700
|
-
import { mkdirSync as
|
|
4174
|
+
import { join as join17, basename as basename5, dirname as dirname6 } from "path";
|
|
4175
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5, existsSync as existsSync18 } from "fs";
|
|
3701
4176
|
function decidePath4(ctx, intent, providedName) {
|
|
3702
4177
|
const rawName = providedName || "";
|
|
3703
4178
|
let decisionPath = "standalone";
|
|
@@ -3711,7 +4186,7 @@ function decidePath4(ctx, intent, providedName) {
|
|
|
3711
4186
|
targetDir = ctx.cwd;
|
|
3712
4187
|
shouldCreateDir = false;
|
|
3713
4188
|
} else {
|
|
3714
|
-
targetDir =
|
|
4189
|
+
targetDir = join17(ctx.cwd, rawName);
|
|
3715
4190
|
shouldCreateDir = true;
|
|
3716
4191
|
}
|
|
3717
4192
|
migrateExisting = ctx.looseSkillFiles.length > 0;
|
|
@@ -3721,7 +4196,7 @@ function decidePath4(ctx, intent, providedName) {
|
|
|
3721
4196
|
targetDir = ctx.cwd;
|
|
3722
4197
|
shouldCreateDir = false;
|
|
3723
4198
|
} else {
|
|
3724
|
-
targetDir =
|
|
4199
|
+
targetDir = join17(ctx.cwd, rawName);
|
|
3725
4200
|
shouldCreateDir = true;
|
|
3726
4201
|
}
|
|
3727
4202
|
} else if (decisionPath === "standalone") {
|
|
@@ -3729,7 +4204,7 @@ function decidePath4(ctx, intent, providedName) {
|
|
|
3729
4204
|
targetDir = ctx.cwd;
|
|
3730
4205
|
shouldCreateDir = false;
|
|
3731
4206
|
} else {
|
|
3732
|
-
targetDir =
|
|
4207
|
+
targetDir = join17(ctx.cwd, rawName);
|
|
3733
4208
|
shouldCreateDir = true;
|
|
3734
4209
|
}
|
|
3735
4210
|
}
|
|
@@ -3737,28 +4212,29 @@ function decidePath4(ctx, intent, providedName) {
|
|
|
3737
4212
|
}
|
|
3738
4213
|
function scaffold4(decision, ctx, migrateContent) {
|
|
3739
4214
|
const { targetDir, path, shouldCreateDir } = decision;
|
|
3740
|
-
if (
|
|
4215
|
+
if (existsSync18(targetDir) && shouldCreateDir) {
|
|
3741
4216
|
ui.fail("Target already exists");
|
|
3742
4217
|
process.exit(1);
|
|
3743
4218
|
}
|
|
3744
4219
|
if (shouldCreateDir) {
|
|
3745
|
-
|
|
4220
|
+
mkdirSync6(targetDir, { recursive: true });
|
|
3746
4221
|
}
|
|
3747
4222
|
if (path === "plugin") {
|
|
3748
4223
|
const pluginName = basename5(targetDir);
|
|
3749
4224
|
const copilotSpec = getProviderSpec("copilot");
|
|
3750
|
-
const copilotManifestDir =
|
|
4225
|
+
const copilotManifestDir = dirname6(copilotSpec.manifestPath);
|
|
3751
4226
|
const pluginJson = {
|
|
3752
4227
|
name: pluginName,
|
|
3753
4228
|
version: "0.1.0",
|
|
3754
4229
|
description: "Scaffolded by doraval copilot new",
|
|
3755
4230
|
skills: ["./skills/doraval"],
|
|
3756
|
-
displayName: pluginName
|
|
4231
|
+
displayName: pluginName,
|
|
4232
|
+
keywords: ["example-keyword", "another-keyword"]
|
|
3757
4233
|
};
|
|
3758
|
-
|
|
3759
|
-
writeFileSync5(
|
|
3760
|
-
const marketplaceDir =
|
|
3761
|
-
|
|
4234
|
+
mkdirSync6(join17(targetDir, copilotManifestDir), { recursive: true });
|
|
4235
|
+
writeFileSync5(join17(targetDir, copilotSpec.manifestPath), JSON.stringify(pluginJson, null, 2));
|
|
4236
|
+
const marketplaceDir = dirname6(copilotSpec.marketplacePath);
|
|
4237
|
+
mkdirSync6(join17(targetDir, marketplaceDir), { recursive: true });
|
|
3762
4238
|
const marketplaceJson = {
|
|
3763
4239
|
name: "local",
|
|
3764
4240
|
plugins: [
|
|
@@ -3771,9 +4247,9 @@ function scaffold4(decision, ctx, migrateContent) {
|
|
|
3771
4247
|
}
|
|
3772
4248
|
]
|
|
3773
4249
|
};
|
|
3774
|
-
writeFileSync5(
|
|
4250
|
+
writeFileSync5(join17(targetDir, copilotSpec.marketplacePath), JSON.stringify(marketplaceJson, null, 2));
|
|
3775
4251
|
const demoSkillName = "doraval";
|
|
3776
|
-
|
|
4252
|
+
mkdirSync6(join17(targetDir, "skills", demoSkillName), { recursive: true });
|
|
3777
4253
|
let skillContent;
|
|
3778
4254
|
if (migrateContent) {
|
|
3779
4255
|
skillContent = migrateContent;
|
|
@@ -3802,19 +4278,19 @@ To test in Copilot:
|
|
|
3802
4278
|
1. Configure the .github/plugin as local source.
|
|
3803
4279
|
2. Restart/reload and invoke the skill.`;
|
|
3804
4280
|
}
|
|
3805
|
-
writeFileSync5(
|
|
3806
|
-
const readmePath =
|
|
3807
|
-
if (!
|
|
4281
|
+
writeFileSync5(join17(targetDir, "skills", demoSkillName, "SKILL.md"), skillContent);
|
|
4282
|
+
const readmePath = join17(targetDir, "README.md");
|
|
4283
|
+
if (!existsSync18(readmePath)) {
|
|
3808
4284
|
writeFileSync5(readmePath, "# " + pluginName + `
|
|
3809
4285
|
|
|
3810
4286
|
Copilot plugin scaffolded by doraval.`);
|
|
3811
4287
|
}
|
|
3812
4288
|
} else {
|
|
3813
|
-
|
|
4289
|
+
mkdirSync6(join17(targetDir, "skills", "doraval"), { recursive: true });
|
|
3814
4290
|
const skillBody = migrateContent || `# My Skill
|
|
3815
4291
|
|
|
3816
4292
|
Basic starter for Copilot.`;
|
|
3817
|
-
writeFileSync5(
|
|
4293
|
+
writeFileSync5(join17(targetDir, "skills", "doraval", "SKILL.md"), `---
|
|
3818
4294
|
name: doraval
|
|
3819
4295
|
description: Starter (local skill)
|
|
3820
4296
|
---
|
|
@@ -3822,14 +4298,14 @@ description: Starter (local skill)
|
|
|
3822
4298
|
${skillBody}`);
|
|
3823
4299
|
}
|
|
3824
4300
|
}
|
|
3825
|
-
var
|
|
4301
|
+
var import_picocolors15, new_default4;
|
|
3826
4302
|
var init_new4 = __esm(() => {
|
|
3827
4303
|
init_dist();
|
|
3828
4304
|
init_out();
|
|
3829
|
-
|
|
4305
|
+
init_context5();
|
|
3830
4306
|
init_prompt();
|
|
3831
4307
|
init_spec();
|
|
3832
|
-
|
|
4308
|
+
import_picocolors15 = __toESM(require_picocolors(), 1);
|
|
3833
4309
|
new_default4 = defineCommand({
|
|
3834
4310
|
meta: {
|
|
3835
4311
|
name: "new",
|
|
@@ -3868,7 +4344,7 @@ var init_new4 = __esm(() => {
|
|
|
3868
4344
|
}
|
|
3869
4345
|
scaffold4(decision, ctx, migrateContent);
|
|
3870
4346
|
ui.write(`
|
|
3871
|
-
${
|
|
4347
|
+
${import_picocolors15.default.green("\u2713")} Created ${decision.path} at ${import_picocolors15.default.bold(decision.targetDir)}`);
|
|
3872
4348
|
const cmdName = decision.path === "plugin" ? `/${basename5(decision.targetDir)}:doraval` : "/doraval (local skill)";
|
|
3873
4349
|
ui.info(` Command: ${cmdName}`);
|
|
3874
4350
|
if (decision.path === "plugin") {
|
|
@@ -3877,6 +4353,9 @@ var init_new4 = __esm(() => {
|
|
|
3877
4353
|
}
|
|
3878
4354
|
ui.info(` Test (local): configure local plugin source in Copilot and reload`);
|
|
3879
4355
|
ui.info(` Validate: doraval validate ${decision.targetDir}`);
|
|
4356
|
+
if (decision.path === "plugin") {
|
|
4357
|
+
ui.info(` Keywords: keywords array added for discovery \u2014 run validate to see "If users mention any of these keywords, your plugin will get triggered"`);
|
|
4358
|
+
}
|
|
3880
4359
|
if (decision.path === "plugin" && decision.migrateExisting) {
|
|
3881
4360
|
ui.info(" (Existing content migrated where confirmed.)");
|
|
3882
4361
|
}
|
|
@@ -3885,8 +4364,260 @@ var init_new4 = __esm(() => {
|
|
|
3885
4364
|
});
|
|
3886
4365
|
});
|
|
3887
4366
|
|
|
4367
|
+
// src/cli/commands/ui.ts
|
|
4368
|
+
var exports_ui = {};
|
|
4369
|
+
__export(exports_ui, {
|
|
4370
|
+
default: () => ui_default
|
|
4371
|
+
});
|
|
4372
|
+
import { existsSync as existsSync19, readdirSync as readdirSync9 } from "fs";
|
|
4373
|
+
import { join as join18 } from "path";
|
|
4374
|
+
import { spawn } from "child_process";
|
|
4375
|
+
function slugify2(title) {
|
|
4376
|
+
return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "untitled";
|
|
4377
|
+
}
|
|
4378
|
+
async function loadAllEntries(project) {
|
|
4379
|
+
const journalsDir = getJournalsDir();
|
|
4380
|
+
const entries = [];
|
|
4381
|
+
const globalPath = join18(journalsDir, "global.md");
|
|
4382
|
+
if (existsSync19(globalPath)) {
|
|
4383
|
+
try {
|
|
4384
|
+
const raw = await Bun.file(globalPath).text();
|
|
4385
|
+
const parsed = parseJournalEntries(raw);
|
|
4386
|
+
parsed.forEach((e) => entries.push({ ...e, _source: "global" }));
|
|
4387
|
+
} catch {}
|
|
4388
|
+
}
|
|
4389
|
+
if (project) {
|
|
4390
|
+
const projPath = join18(journalsDir, `${project}.md`);
|
|
4391
|
+
if (existsSync19(projPath)) {
|
|
4392
|
+
try {
|
|
4393
|
+
const raw = await Bun.file(projPath).text();
|
|
4394
|
+
const parsed = parseJournalEntries(raw);
|
|
4395
|
+
parsed.forEach((e) => entries.push({ ...e, _source: "project" }));
|
|
4396
|
+
} catch {}
|
|
4397
|
+
}
|
|
4398
|
+
}
|
|
4399
|
+
const staged = [];
|
|
4400
|
+
try {
|
|
4401
|
+
const pdir = project ? getPendingProjectDir(project) : null;
|
|
4402
|
+
if (pdir && existsSync19(pdir)) {
|
|
4403
|
+
const files = readdirSync9(pdir).filter((f) => f.endsWith(".md") && f !== ".gitkeep");
|
|
4404
|
+
for (const f of files) {
|
|
4405
|
+
const txt = await Bun.file(join18(pdir, f)).text();
|
|
4406
|
+
const parsed = parseJournalEntries(txt);
|
|
4407
|
+
parsed.forEach((e) => {
|
|
4408
|
+
e._staged = true;
|
|
4409
|
+
e._source = "staged";
|
|
4410
|
+
e._filename = f;
|
|
4411
|
+
staged.push(e);
|
|
4412
|
+
});
|
|
4413
|
+
}
|
|
4414
|
+
}
|
|
4415
|
+
} catch {}
|
|
4416
|
+
return { committed: entries, staged };
|
|
4417
|
+
}
|
|
4418
|
+
async function writePendingEntry(project, input) {
|
|
4419
|
+
ensureDoravalDirs();
|
|
4420
|
+
const pendingDir = getPendingProjectDir(project);
|
|
4421
|
+
if (!existsSync19(pendingDir)) {
|
|
4422
|
+
await Bun.write(join18(pendingDir, ".gitkeep"), "");
|
|
4423
|
+
}
|
|
4424
|
+
const date = new Date().toISOString().split("T")[0];
|
|
4425
|
+
const slug = slugify2(input.title);
|
|
4426
|
+
const filename = `${date}-${slug}.md`;
|
|
4427
|
+
const filePath = join18(pendingDir, filename);
|
|
4428
|
+
const content = `## ${input.title}
|
|
4429
|
+
|
|
4430
|
+
\`\`\`yaml
|
|
4431
|
+
pushback: ${input.pushback}
|
|
4432
|
+
tags: [${input.tags.join(", ")}]
|
|
4433
|
+
author: ${input.author || "human"}
|
|
4434
|
+
date: ${date}
|
|
4435
|
+
status: active
|
|
4436
|
+
\`\`\`
|
|
4437
|
+
|
|
4438
|
+
${input.rationale}
|
|
4439
|
+
`;
|
|
4440
|
+
await Bun.write(filePath, content);
|
|
4441
|
+
return { filePath, filename };
|
|
4442
|
+
}
|
|
4443
|
+
async function killPort(port) {
|
|
4444
|
+
if (process.platform === "win32") {
|
|
4445
|
+
return;
|
|
4446
|
+
}
|
|
4447
|
+
try {
|
|
4448
|
+
const proc = Bun.spawn(["lsof", "-ti", `tcp:${port}`, "-sTCP:LISTEN"], { stdout: "pipe", stderr: "ignore" });
|
|
4449
|
+
const output = (await new Response(proc.stdout).text()).trim();
|
|
4450
|
+
if (!output)
|
|
4451
|
+
return;
|
|
4452
|
+
const pids = output.split(`
|
|
4453
|
+
`).map((p) => p.trim()).filter(Boolean);
|
|
4454
|
+
console.error(` Killing previous doraval ui on port ${port}...`);
|
|
4455
|
+
for (const pid of pids) {
|
|
4456
|
+
console.error(` \u2192 kill -9 ${pid}`);
|
|
4457
|
+
Bun.spawn(["kill", "-9", pid], { stdout: "ignore", stderr: "ignore" });
|
|
4458
|
+
}
|
|
4459
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
4460
|
+
} catch {}
|
|
4461
|
+
}
|
|
4462
|
+
async function getDashboardHtml() {
|
|
4463
|
+
const isSource = import.meta.url.includes("/src/");
|
|
4464
|
+
const htmlPath = isSource ? new URL("../../ui/index.html", import.meta.url) : new URL("./ui/index.html", import.meta.url);
|
|
4465
|
+
try {
|
|
4466
|
+
return await Bun.file(htmlPath).text();
|
|
4467
|
+
} catch (err) {
|
|
4468
|
+
console.error(`[doraval ui] Failed to load HTML from ${htmlPath}`);
|
|
4469
|
+
return `<!doctype html><meta charset="utf-8"><body style="font-family:monospace;background:#111;color:#ddd;padding:2rem"><h1>doraval ui</h1><p>Dashboard HTML missing.</p><pre>${String(err)}</pre></body>`;
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
var import_picocolors16, DEFAULT_PORT = 3737, ui_default;
|
|
4473
|
+
var init_ui = __esm(() => {
|
|
4474
|
+
init_journal_config();
|
|
4475
|
+
init_journal_parse();
|
|
4476
|
+
init_context();
|
|
4477
|
+
init_hook();
|
|
4478
|
+
import_picocolors16 = __toESM(require_picocolors(), 1);
|
|
4479
|
+
ui_default = {
|
|
4480
|
+
async run({ args }) {
|
|
4481
|
+
const port = Number(args.port) || DEFAULT_PORT;
|
|
4482
|
+
const host = args.host || "127.0.0.1";
|
|
4483
|
+
const shouldOpen = args.open !== false;
|
|
4484
|
+
await killPort(port);
|
|
4485
|
+
const config = await readConfig();
|
|
4486
|
+
let project = resolveProjectName(config) ?? undefined;
|
|
4487
|
+
if (project) {
|
|
4488
|
+
try {
|
|
4489
|
+
project = sanitizeProjectName(project);
|
|
4490
|
+
} catch {
|
|
4491
|
+
project = undefined;
|
|
4492
|
+
}
|
|
4493
|
+
}
|
|
4494
|
+
const server = Bun.serve({
|
|
4495
|
+
port,
|
|
4496
|
+
hostname: host,
|
|
4497
|
+
async fetch(req) {
|
|
4498
|
+
const url2 = new URL(req.url);
|
|
4499
|
+
if (url2.pathname === "/" || url2.pathname === "/index.html") {
|
|
4500
|
+
const html = await getDashboardHtml();
|
|
4501
|
+
return new Response(html, {
|
|
4502
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
4503
|
+
});
|
|
4504
|
+
}
|
|
4505
|
+
if (url2.pathname === "/api/status") {
|
|
4506
|
+
return Response.json({
|
|
4507
|
+
project: project || null,
|
|
4508
|
+
doravalDir: getJournalsDir(),
|
|
4509
|
+
hasConfig: !!config,
|
|
4510
|
+
repo: config?.journal?.repo ?? null
|
|
4511
|
+
});
|
|
4512
|
+
}
|
|
4513
|
+
if (url2.pathname === "/api/entries") {
|
|
4514
|
+
const { committed, staged } = await loadAllEntries(project || null);
|
|
4515
|
+
return Response.json({ project, committed, staged });
|
|
4516
|
+
}
|
|
4517
|
+
if (url2.pathname === "/api/context") {
|
|
4518
|
+
const { committed, staged } = await loadAllEntries(project || null);
|
|
4519
|
+
const all = [...staged, ...committed].filter((e) => (e.status || "active") === "active");
|
|
4520
|
+
const text = generateJournalContext(all, project || null, { minPushback: 1 });
|
|
4521
|
+
return Response.json({ text, project });
|
|
4522
|
+
}
|
|
4523
|
+
if (url2.pathname === "/api/hooks/status" && req.method === "GET") {
|
|
4524
|
+
const localPath = getLocalHooksPath();
|
|
4525
|
+
const globalPath = getGlobalSettingsPath();
|
|
4526
|
+
const localHas = hasHook(await readJson(localPath));
|
|
4527
|
+
const globalHas = hasHook(await readJson(globalPath));
|
|
4528
|
+
return Response.json({
|
|
4529
|
+
local: { enabled: localHas, path: localPath },
|
|
4530
|
+
global: { enabled: globalHas, path: globalPath }
|
|
4531
|
+
});
|
|
4532
|
+
}
|
|
4533
|
+
if (url2.pathname === "/api/hooks/enable" && req.method === "POST") {
|
|
4534
|
+
const body = await req.json().catch(() => ({}));
|
|
4535
|
+
const useGlobal = !!body.global;
|
|
4536
|
+
const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
|
|
4537
|
+
const res = await addHook(target);
|
|
4538
|
+
return Response.json(res);
|
|
4539
|
+
}
|
|
4540
|
+
if (url2.pathname === "/api/hooks/disable" && req.method === "POST") {
|
|
4541
|
+
const body = await req.json().catch(() => ({}));
|
|
4542
|
+
const useGlobal = !!body.global;
|
|
4543
|
+
const target = useGlobal ? getGlobalSettingsPath() : getLocalHooksPath();
|
|
4544
|
+
const res = await removeHook(target);
|
|
4545
|
+
return Response.json(res);
|
|
4546
|
+
}
|
|
4547
|
+
if (url2.pathname === "/api/add" && req.method === "POST") {
|
|
4548
|
+
if (!project) {
|
|
4549
|
+
return Response.json({ error: "No project configured. Run dora init or dora journal init first." }, { status: 400 });
|
|
4550
|
+
}
|
|
4551
|
+
const body = await req.json();
|
|
4552
|
+
const title = String(body.title || "Untitled decision").trim();
|
|
4553
|
+
const pushback = Number(body.pushback ?? 4);
|
|
4554
|
+
const tags = Array.isArray(body.tags) ? body.tags.map((t) => String(t).trim()).filter(Boolean) : [];
|
|
4555
|
+
const rationale = String(body.rationale || title).trim();
|
|
4556
|
+
try {
|
|
4557
|
+
const result = await writePendingEntry(project, { title, pushback, tags, rationale });
|
|
4558
|
+
return Response.json({ ok: true, ...result });
|
|
4559
|
+
} catch (e) {
|
|
4560
|
+
return Response.json({ error: e.message }, { status: 500 });
|
|
4561
|
+
}
|
|
4562
|
+
}
|
|
4563
|
+
if (url2.pathname === "/api/refresh" && req.method === "POST") {
|
|
4564
|
+
const { committed, staged } = await loadAllEntries(project || null);
|
|
4565
|
+
return Response.json({ ok: true, committed, staged });
|
|
4566
|
+
}
|
|
4567
|
+
if (url2.pathname === "/api/delete-staged" && req.method === "POST") {
|
|
4568
|
+
if (!project) {
|
|
4569
|
+
return Response.json({ error: "No project" }, { status: 400 });
|
|
4570
|
+
}
|
|
4571
|
+
const body = await req.json().catch(() => ({}));
|
|
4572
|
+
const filename = body.filename;
|
|
4573
|
+
if (!filename) {
|
|
4574
|
+
return Response.json({ error: "filename required" }, { status: 400 });
|
|
4575
|
+
}
|
|
4576
|
+
const pdir = getPendingProjectDir(project);
|
|
4577
|
+
const filePath = join18(pdir, filename);
|
|
4578
|
+
if (existsSync19(filePath)) {
|
|
4579
|
+
try {
|
|
4580
|
+
await Bun.file(filePath).unlink();
|
|
4581
|
+
} catch {}
|
|
4582
|
+
return Response.json({ ok: true });
|
|
4583
|
+
}
|
|
4584
|
+
return Response.json({ error: "not found" }, { status: 404 });
|
|
4585
|
+
}
|
|
4586
|
+
if (url2.pathname.startsWith("/api/")) {
|
|
4587
|
+
return Response.json({ error: "Not found" }, { status: 404 });
|
|
4588
|
+
}
|
|
4589
|
+
return new Response("Not found", { status: 404 });
|
|
4590
|
+
}
|
|
4591
|
+
});
|
|
4592
|
+
const url = `http://${host === "0.0.0.0" ? "localhost" : host}:${server.port}`;
|
|
4593
|
+
const msg = `
|
|
4594
|
+
${import_picocolors16.default.blue("\u25C9")} dora local dashboard
|
|
4595
|
+
${import_picocolors16.default.dim("Project:")} ${project ? import_picocolors16.default.white(project) : import_picocolors16.default.yellow("none (run dora init)")}
|
|
4596
|
+
${import_picocolors16.default.dim("URL:")} ${import_picocolors16.default.underline(import_picocolors16.default.cyan(url))}
|
|
4597
|
+
|
|
4598
|
+
${import_picocolors16.default.dim("Press Ctrl+C to stop")}
|
|
4599
|
+
`;
|
|
4600
|
+
console.error(msg);
|
|
4601
|
+
if (shouldOpen && process.stdout.isTTY) {
|
|
4602
|
+
try {
|
|
4603
|
+
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
4604
|
+
spawn(opener, [url], { stdio: "ignore", detached: true }).unref();
|
|
4605
|
+
} catch {
|
|
4606
|
+
console.error(import_picocolors16.default.dim(` Could not auto-open. Visit ${url}`));
|
|
4607
|
+
}
|
|
4608
|
+
}
|
|
4609
|
+
process.on("SIGINT", () => {
|
|
4610
|
+
console.error(`
|
|
4611
|
+
Stopping dashboard...`);
|
|
4612
|
+
server.stop();
|
|
4613
|
+
process.exit(0);
|
|
4614
|
+
});
|
|
4615
|
+
}
|
|
4616
|
+
};
|
|
4617
|
+
});
|
|
4618
|
+
|
|
3888
4619
|
// src/validators/claude/skill.ts
|
|
3889
|
-
import { existsSync as
|
|
4620
|
+
import { existsSync as existsSync20 } from "fs";
|
|
3890
4621
|
import { resolve as resolve5 } from "path";
|
|
3891
4622
|
var claudeSkillValidator;
|
|
3892
4623
|
var init_skill = __esm(() => {
|
|
@@ -3897,7 +4628,7 @@ var init_skill = __esm(() => {
|
|
|
3897
4628
|
name: "Claude Skill",
|
|
3898
4629
|
description: "Validates SKILL.md per current Claude Code spec: frontmatter (name/description relaxed to recommended; directory name usually provides the /command), body, supporting files, dynamic injection (!`cmd`), substitutions ($ARGUMENTS, ${CLAUDE_*}), and advanced fields (allowed-tools, context, disable-model-invocation, when_to_use, etc.)",
|
|
3899
4630
|
detect(dir) {
|
|
3900
|
-
return
|
|
4631
|
+
return existsSync20(resolve5(dir, "SKILL.md"));
|
|
3901
4632
|
},
|
|
3902
4633
|
async validate(dir, _opts) {
|
|
3903
4634
|
const loaded = await loadSkill(dir);
|
|
@@ -3915,8 +4646,8 @@ var init_skill = __esm(() => {
|
|
|
3915
4646
|
});
|
|
3916
4647
|
|
|
3917
4648
|
// src/validators/claude/plugin.ts
|
|
3918
|
-
import { existsSync as
|
|
3919
|
-
import { resolve as resolve6, join as
|
|
4649
|
+
import { existsSync as existsSync21, readdirSync as readdirSync10 } from "fs";
|
|
4650
|
+
import { resolve as resolve6, join as join19 } from "path";
|
|
3920
4651
|
function levenshtein(a, b) {
|
|
3921
4652
|
if (a === b)
|
|
3922
4653
|
return 0;
|
|
@@ -4008,7 +4739,7 @@ var init_plugin = __esm(() => {
|
|
|
4008
4739
|
name: "Claude Plugin",
|
|
4009
4740
|
description: "Validates .claude-plugin/plugin.json manifest (complete schema per Plugins reference), component path rules (replace vs augment), .claude-plugin/ purity, default dirs, single-root-skill layout, unrecognized fields + suggestions, and structure",
|
|
4010
4741
|
detect(dir) {
|
|
4011
|
-
return
|
|
4742
|
+
return existsSync21(resolve6(dir, ".claude-plugin", "plugin.json"));
|
|
4012
4743
|
},
|
|
4013
4744
|
async validate(dir, _opts) {
|
|
4014
4745
|
const errors = [];
|
|
@@ -4021,12 +4752,17 @@ var init_plugin = __esm(() => {
|
|
|
4021
4752
|
const raw = await Bun.file(manifestPath).text();
|
|
4022
4753
|
manifest = JSON.parse(raw);
|
|
4023
4754
|
passes.push(".claude-plugin/plugin.json is valid JSON");
|
|
4024
|
-
} catch {
|
|
4025
|
-
|
|
4755
|
+
} catch (err) {
|
|
4756
|
+
if (!existsSync21(manifestPath)) {
|
|
4757
|
+
errors.push(`.claude-plugin/plugin.json is missing (looked for ${manifestPath})`);
|
|
4758
|
+
warnings.push("Hint: Run `doraval claude new` (or `dora claude new`) to scaffold a new Claude plugin in this directory.");
|
|
4759
|
+
} else {
|
|
4760
|
+
errors.push(`.claude-plugin/plugin.json is invalid JSON (${err.message})`);
|
|
4761
|
+
}
|
|
4026
4762
|
return { errors, warnings, passes };
|
|
4027
4763
|
}
|
|
4028
4764
|
try {
|
|
4029
|
-
const entries =
|
|
4765
|
+
const entries = readdirSync10(dotClaudePluginDir);
|
|
4030
4766
|
const unexpected = entries.filter((e) => e !== "plugin.json");
|
|
4031
4767
|
if (unexpected.length > 0) {
|
|
4032
4768
|
for (const e of unexpected) {
|
|
@@ -4082,10 +4818,12 @@ var init_plugin = __esm(() => {
|
|
|
4082
4818
|
}
|
|
4083
4819
|
if (manifest.keywords !== undefined) {
|
|
4084
4820
|
if (Array.isArray(manifest.keywords)) {
|
|
4085
|
-
passes.push(`keywords: [${manifest.keywords.join(", ")}]`);
|
|
4821
|
+
passes.push(`keywords: [${manifest.keywords.join(", ")}] \u2014 If users mention any of these keywords, your plugin will get triggered in Claude Code`);
|
|
4086
4822
|
} else {
|
|
4087
4823
|
errors.push("keywords must be an array of strings");
|
|
4088
4824
|
}
|
|
4825
|
+
} else {
|
|
4826
|
+
warnings.push('Missing "keywords" (recommended \u2014 if users mention any of these, your plugin will get triggered in Claude Code)');
|
|
4089
4827
|
}
|
|
4090
4828
|
if (manifest.defaultEnabled !== undefined) {
|
|
4091
4829
|
passes.push(`defaultEnabled: ${manifest.defaultEnabled}`);
|
|
@@ -4111,7 +4849,7 @@ var init_plugin = __esm(() => {
|
|
|
4111
4849
|
errors.push(`${field}: path "${s}" must start with "./"`);
|
|
4112
4850
|
} else if (s.includes("..")) {
|
|
4113
4851
|
errors.push(`${field}: path "${s}" must not use ".." (paths are confined to the plugin tree after cache copy)`);
|
|
4114
|
-
} else if (
|
|
4852
|
+
} else if (existsSync21(resolve6(dir, s))) {
|
|
4115
4853
|
passes.push(`${field}: path "${s}" exists`);
|
|
4116
4854
|
} else {
|
|
4117
4855
|
warnings.push(`${field}: path "${s}" does not exist on disk`);
|
|
@@ -4161,11 +4899,11 @@ var init_plugin = __esm(() => {
|
|
|
4161
4899
|
passes.push(`dependencies: declares ${manifest.dependencies.length} plugin dependency/ies`);
|
|
4162
4900
|
}
|
|
4163
4901
|
const skillsDir = resolve6(dir, "skills");
|
|
4164
|
-
if (
|
|
4165
|
-
const entries =
|
|
4902
|
+
if (existsSync21(skillsDir)) {
|
|
4903
|
+
const entries = readdirSync10(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
4166
4904
|
for (const e of entries) {
|
|
4167
|
-
const md =
|
|
4168
|
-
if (
|
|
4905
|
+
const md = join19(skillsDir, e.name, "SKILL.md");
|
|
4906
|
+
if (existsSync21(md)) {
|
|
4169
4907
|
passes.push(`skills/${e.name}/SKILL.md exists`);
|
|
4170
4908
|
} else {
|
|
4171
4909
|
errors.push(`skills/${e.name}/ is missing SKILL.md`);
|
|
@@ -4176,8 +4914,8 @@ var init_plugin = __esm(() => {
|
|
|
4176
4914
|
}
|
|
4177
4915
|
}
|
|
4178
4916
|
const commandsDir = resolve6(dir, "commands");
|
|
4179
|
-
if (
|
|
4180
|
-
const mds =
|
|
4917
|
+
if (existsSync21(commandsDir)) {
|
|
4918
|
+
const mds = readdirSync10(commandsDir).filter((f) => f.endsWith(".md"));
|
|
4181
4919
|
if (mds.length) {
|
|
4182
4920
|
passes.push(`commands/ has ${mds.length} .md file(s)`);
|
|
4183
4921
|
}
|
|
@@ -4186,8 +4924,8 @@ var init_plugin = __esm(() => {
|
|
|
4186
4924
|
}
|
|
4187
4925
|
}
|
|
4188
4926
|
const agentsDir = resolve6(dir, "agents");
|
|
4189
|
-
if (
|
|
4190
|
-
const mds =
|
|
4927
|
+
if (existsSync21(agentsDir)) {
|
|
4928
|
+
const mds = readdirSync10(agentsDir).filter((f) => f.endsWith(".md"));
|
|
4191
4929
|
if (mds.length) {
|
|
4192
4930
|
passes.push(`agents/ has ${mds.length} .md file(s)`);
|
|
4193
4931
|
}
|
|
@@ -4195,30 +4933,30 @@ var init_plugin = __esm(() => {
|
|
|
4195
4933
|
warnings.push('agents/ co-exists with manifest "agents" \u2014 manifest replaces default (dir ignored)');
|
|
4196
4934
|
}
|
|
4197
4935
|
}
|
|
4198
|
-
if (
|
|
4936
|
+
if (existsSync21(resolve6(dir, "output-styles"))) {
|
|
4199
4937
|
passes.push("output-styles/ directory present");
|
|
4200
4938
|
if (manifest.outputStyles)
|
|
4201
4939
|
warnings.push("output-styles/ co-exists with manifest outputStyles \u2014 manifest wins");
|
|
4202
4940
|
}
|
|
4203
|
-
if (
|
|
4941
|
+
if (existsSync21(resolve6(dir, "themes")))
|
|
4204
4942
|
passes.push("themes/ present (experimental)");
|
|
4205
|
-
if (
|
|
4943
|
+
if (existsSync21(resolve6(dir, "monitors")) || manifest.experimental?.monitors) {
|
|
4206
4944
|
passes.push("monitors config present (experimental)");
|
|
4207
4945
|
}
|
|
4208
|
-
if (
|
|
4946
|
+
if (existsSync21(resolve6(dir, "bin")))
|
|
4209
4947
|
passes.push("bin/ present (adds executables to Bash tool $PATH)");
|
|
4210
|
-
if (
|
|
4948
|
+
if (existsSync21(resolve6(dir, "settings.json")))
|
|
4211
4949
|
passes.push("settings.json present (plugin defaults for agent/statusline)");
|
|
4212
|
-
if (
|
|
4950
|
+
if (existsSync21(resolve6(dir, "README.md")))
|
|
4213
4951
|
passes.push("README.md present");
|
|
4214
|
-
if (
|
|
4952
|
+
if (existsSync21(resolve6(dir, ".mcp.json")))
|
|
4215
4953
|
passes.push(".mcp.json present (validated by claude:mcp)");
|
|
4216
|
-
if (
|
|
4954
|
+
if (existsSync21(resolve6(dir, ".lsp.json")))
|
|
4217
4955
|
passes.push(".lsp.json present (validated by claude:lsp when registered)");
|
|
4218
|
-
if (
|
|
4956
|
+
if (existsSync21(resolve6(dir, "hooks/hooks.json")) || existsSync21(resolve6(dir, "hooks.json"))) {
|
|
4219
4957
|
passes.push("hooks config present (validated by claude:hooks)");
|
|
4220
4958
|
}
|
|
4221
|
-
if (
|
|
4959
|
+
if (existsSync21(resolve6(dir, "SKILL.md")) && !existsSync21(skillsDir) && manifest.skills === undefined) {
|
|
4222
4960
|
passes.push('Root SKILL.md detected \u2014 plugin will be treated as a single-skill plugin (prefer frontmatter "name" for stable /command)');
|
|
4223
4961
|
}
|
|
4224
4962
|
return { errors, warnings, passes };
|
|
@@ -4227,8 +4965,8 @@ var init_plugin = __esm(() => {
|
|
|
4227
4965
|
});
|
|
4228
4966
|
|
|
4229
4967
|
// src/validators/claude/marketplace.ts
|
|
4230
|
-
import { existsSync as
|
|
4231
|
-
import { resolve as resolve7, join as
|
|
4968
|
+
import { existsSync as existsSync22, readdirSync as readdirSync11 } from "fs";
|
|
4969
|
+
import { resolve as resolve7, join as join20 } from "path";
|
|
4232
4970
|
var claudeMarketplaceValidator;
|
|
4233
4971
|
var init_marketplace = __esm(() => {
|
|
4234
4972
|
claudeMarketplaceValidator = {
|
|
@@ -4237,18 +4975,18 @@ var init_marketplace = __esm(() => {
|
|
|
4237
4975
|
name: "Claude Plugin Marketplace",
|
|
4238
4976
|
description: "Validates .claude-plugin/marketplace.json or plugins/ marketplace layouts (plugins array with sources)",
|
|
4239
4977
|
detect(dir) {
|
|
4240
|
-
if (
|
|
4978
|
+
if (existsSync22(resolve7(dir, ".claude-plugin", "marketplace.json")))
|
|
4241
4979
|
return true;
|
|
4242
4980
|
const pluginsDir = resolve7(dir, "plugins");
|
|
4243
|
-
if (!
|
|
4981
|
+
if (!existsSync22(pluginsDir))
|
|
4244
4982
|
return false;
|
|
4245
4983
|
try {
|
|
4246
|
-
const entries =
|
|
4984
|
+
const entries = readdirSync11(pluginsDir, { withFileTypes: true });
|
|
4247
4985
|
for (const entry of entries) {
|
|
4248
4986
|
if (!entry.isDirectory())
|
|
4249
4987
|
continue;
|
|
4250
|
-
const hasSkills =
|
|
4251
|
-
const hasManifest =
|
|
4988
|
+
const hasSkills = existsSync22(join20(pluginsDir, entry.name, "skills"));
|
|
4989
|
+
const hasManifest = existsSync22(join20(pluginsDir, entry.name, ".claude-plugin", "plugin.json"));
|
|
4252
4990
|
if (hasSkills || hasManifest)
|
|
4253
4991
|
return true;
|
|
4254
4992
|
}
|
|
@@ -4260,9 +4998,9 @@ var init_marketplace = __esm(() => {
|
|
|
4260
4998
|
const warnings = [];
|
|
4261
4999
|
const passes = [];
|
|
4262
5000
|
const claudeMktPath = resolve7(dir, ".claude-plugin", "marketplace.json");
|
|
4263
|
-
const hasClaudeMkt =
|
|
5001
|
+
const hasClaudeMkt = existsSync22(claudeMktPath);
|
|
4264
5002
|
const pluginsDir = resolve7(dir, "plugins");
|
|
4265
|
-
const hasPluginsDirLayout =
|
|
5003
|
+
const hasPluginsDirLayout = existsSync22(pluginsDir);
|
|
4266
5004
|
if (!hasClaudeMkt && !hasPluginsDirLayout) {
|
|
4267
5005
|
errors.push("Missing .claude-plugin/marketplace.json or plugins/ directory");
|
|
4268
5006
|
return { errors, warnings, passes };
|
|
@@ -4307,9 +5045,9 @@ var init_marketplace = __esm(() => {
|
|
|
4307
5045
|
const src = String(p.source);
|
|
4308
5046
|
passes.push(`plugins[${i}].source: "${src}"`);
|
|
4309
5047
|
const srcDir = resolve7(dir, src);
|
|
4310
|
-
if (
|
|
4311
|
-
const hasManifest =
|
|
4312
|
-
const hasSkills =
|
|
5048
|
+
if (existsSync22(srcDir)) {
|
|
5049
|
+
const hasManifest = existsSync22(resolve7(srcDir, ".claude-plugin", "plugin.json"));
|
|
5050
|
+
const hasSkills = existsSync22(resolve7(srcDir, "skills"));
|
|
4313
5051
|
if (hasManifest || hasSkills) {
|
|
4314
5052
|
passes.push(`plugins[${i}]: source exists (${hasManifest ? "manifest" : "skills/"})`);
|
|
4315
5053
|
} else {
|
|
@@ -4325,12 +5063,12 @@ var init_marketplace = __esm(() => {
|
|
|
4325
5063
|
passes.push(`plugins[${i}].category: "${p.category}"`);
|
|
4326
5064
|
}
|
|
4327
5065
|
}
|
|
4328
|
-
if (
|
|
5066
|
+
if (existsSync22(resolve7(dir, "README.md"))) {
|
|
4329
5067
|
passes.push("README.md exists at marketplace root");
|
|
4330
5068
|
} else {
|
|
4331
5069
|
warnings.push("No README.md at marketplace root \u2014 recommended for discoverability");
|
|
4332
5070
|
}
|
|
4333
|
-
if (
|
|
5071
|
+
if (existsSync22(resolve7(dir, "LICENSE"))) {
|
|
4334
5072
|
passes.push("LICENSE exists at marketplace root");
|
|
4335
5073
|
} else {
|
|
4336
5074
|
warnings.push("No LICENSE at marketplace root \u2014 recommended");
|
|
@@ -4339,27 +5077,27 @@ var init_marketplace = __esm(() => {
|
|
|
4339
5077
|
}
|
|
4340
5078
|
if (hasPluginsDirLayout) {
|
|
4341
5079
|
passes.push("plugins/ directory exists");
|
|
4342
|
-
const pluginEntries =
|
|
5080
|
+
const pluginEntries = readdirSync11(pluginsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
4343
5081
|
if (pluginEntries.length === 0) {
|
|
4344
5082
|
errors.push("plugins/ directory is empty \u2014 expected at least one plugin");
|
|
4345
5083
|
return { errors, warnings, passes };
|
|
4346
5084
|
}
|
|
4347
5085
|
passes.push(`${pluginEntries.length} plugin(s) found`);
|
|
4348
|
-
if (
|
|
5086
|
+
if (existsSync22(resolve7(dir, "README.md"))) {
|
|
4349
5087
|
passes.push("README.md exists at marketplace root");
|
|
4350
5088
|
} else {
|
|
4351
5089
|
warnings.push("No README.md at marketplace root \u2014 recommended for discoverability");
|
|
4352
5090
|
}
|
|
4353
|
-
if (
|
|
5091
|
+
if (existsSync22(resolve7(dir, "LICENSE"))) {
|
|
4354
5092
|
passes.push("LICENSE exists at marketplace root");
|
|
4355
5093
|
} else {
|
|
4356
5094
|
warnings.push("No LICENSE at marketplace root \u2014 recommended");
|
|
4357
5095
|
}
|
|
4358
5096
|
for (const plugin of pluginEntries) {
|
|
4359
|
-
const pluginPath =
|
|
4360
|
-
const hasSkills =
|
|
4361
|
-
const hasManifest =
|
|
4362
|
-
const hasReadme =
|
|
5097
|
+
const pluginPath = join20(pluginsDir, plugin.name);
|
|
5098
|
+
const hasSkills = existsSync22(join20(pluginPath, "skills"));
|
|
5099
|
+
const hasManifest = existsSync22(join20(pluginPath, ".claude-plugin", "plugin.json"));
|
|
5100
|
+
const hasReadme = existsSync22(join20(pluginPath, "README.md"));
|
|
4363
5101
|
if (hasManifest || hasSkills) {
|
|
4364
5102
|
passes.push(`Plugin "${plugin.name}" has ${hasManifest ? "manifest" : "skills/"}`);
|
|
4365
5103
|
} else {
|
|
@@ -4377,7 +5115,7 @@ var init_marketplace = __esm(() => {
|
|
|
4377
5115
|
});
|
|
4378
5116
|
|
|
4379
5117
|
// src/validators/claude/hooks.ts
|
|
4380
|
-
import { existsSync as
|
|
5118
|
+
import { existsSync as existsSync23 } from "fs";
|
|
4381
5119
|
import { resolve as resolve8 } from "path";
|
|
4382
5120
|
var KNOWN_EVENTS, claudeHooksValidator;
|
|
4383
5121
|
var init_hooks = __esm(() => {
|
|
@@ -4419,13 +5157,13 @@ var init_hooks = __esm(() => {
|
|
|
4419
5157
|
name: "Claude Hooks",
|
|
4420
5158
|
description: "Validates hooks/hooks.json (or root hooks.json): all lifecycle events per Plugins reference, hook group structure (matcher + hooks[]), supported hook types (command, http, mcp_tool, prompt, agent)",
|
|
4421
5159
|
detect(dir) {
|
|
4422
|
-
return
|
|
5160
|
+
return existsSync23(resolve8(dir, "hooks", "hooks.json")) || existsSync23(resolve8(dir, "hooks.json"));
|
|
4423
5161
|
},
|
|
4424
5162
|
async validate(dir, _opts) {
|
|
4425
5163
|
const errors = [];
|
|
4426
5164
|
const warnings = [];
|
|
4427
5165
|
const passes = [];
|
|
4428
|
-
const hooksPath =
|
|
5166
|
+
const hooksPath = existsSync23(resolve8(dir, "hooks", "hooks.json")) ? resolve8(dir, "hooks", "hooks.json") : resolve8(dir, "hooks.json");
|
|
4429
5167
|
let config;
|
|
4430
5168
|
try {
|
|
4431
5169
|
const raw = await Bun.file(hooksPath).text();
|
|
@@ -4491,7 +5229,7 @@ var init_hooks = __esm(() => {
|
|
|
4491
5229
|
});
|
|
4492
5230
|
|
|
4493
5231
|
// src/validators/claude/mcp.ts
|
|
4494
|
-
import { existsSync as
|
|
5232
|
+
import { existsSync as existsSync24 } from "fs";
|
|
4495
5233
|
import { resolve as resolve9 } from "path";
|
|
4496
5234
|
var claudeMcpValidator;
|
|
4497
5235
|
var init_mcp = __esm(() => {
|
|
@@ -4501,7 +5239,7 @@ var init_mcp = __esm(() => {
|
|
|
4501
5239
|
name: "Claude MCP Config",
|
|
4502
5240
|
description: "Validates .mcp.json (or inline via plugin.json mcpServers): server entries (stdio: command+args, or url), env, cwd, ${CLAUDE_PLUGIN_ROOT} etc. substitutions per Plugins reference",
|
|
4503
5241
|
detect(dir) {
|
|
4504
|
-
return
|
|
5242
|
+
return existsSync24(resolve9(dir, ".mcp.json"));
|
|
4505
5243
|
},
|
|
4506
5244
|
async validate(dir, _opts) {
|
|
4507
5245
|
const errors = [];
|
|
@@ -4561,8 +5299,8 @@ var init_mcp = __esm(() => {
|
|
|
4561
5299
|
});
|
|
4562
5300
|
|
|
4563
5301
|
// src/validators/claude/subagent.ts
|
|
4564
|
-
import { existsSync as
|
|
4565
|
-
import { resolve as resolve10, join as
|
|
5302
|
+
import { existsSync as existsSync25, readdirSync as readdirSync12 } from "fs";
|
|
5303
|
+
import { resolve as resolve10, join as join21 } from "path";
|
|
4566
5304
|
var claudeSubagentValidator;
|
|
4567
5305
|
var init_subagent = __esm(() => {
|
|
4568
5306
|
init_frontmatter();
|
|
@@ -4573,10 +5311,10 @@ var init_subagent = __esm(() => {
|
|
|
4573
5311
|
description: "Validates agents/*.md (plugin subagents): frontmatter per spec (name, description, model, effort, maxTurns, tools, disallowedTools, skills, memory, background, isolation=worktree), body; warns on disallowed fields (hooks, mcpServers, permissionMode) for security",
|
|
4574
5312
|
detect(dir) {
|
|
4575
5313
|
const agentsDir = resolve10(dir, "agents");
|
|
4576
|
-
if (!
|
|
5314
|
+
if (!existsSync25(agentsDir))
|
|
4577
5315
|
return false;
|
|
4578
5316
|
try {
|
|
4579
|
-
return
|
|
5317
|
+
return readdirSync12(agentsDir).some((f) => f.endsWith(".md"));
|
|
4580
5318
|
} catch {
|
|
4581
5319
|
return false;
|
|
4582
5320
|
}
|
|
@@ -4586,7 +5324,7 @@ var init_subagent = __esm(() => {
|
|
|
4586
5324
|
const warnings = [];
|
|
4587
5325
|
const passes = [];
|
|
4588
5326
|
const agentsDir = resolve10(dir, "agents");
|
|
4589
|
-
const mdFiles =
|
|
5327
|
+
const mdFiles = readdirSync12(agentsDir).filter((f) => f.endsWith(".md"));
|
|
4590
5328
|
if (mdFiles.length === 0) {
|
|
4591
5329
|
errors.push("agents/ directory has no .md files");
|
|
4592
5330
|
return { errors, warnings, passes };
|
|
@@ -4607,7 +5345,7 @@ var init_subagent = __esm(() => {
|
|
|
4607
5345
|
]);
|
|
4608
5346
|
const DISALLOWED = new Set(["hooks", "mcpServers", "permissionMode"]);
|
|
4609
5347
|
for (const file of mdFiles) {
|
|
4610
|
-
const filePath =
|
|
5348
|
+
const filePath = join21(agentsDir, file);
|
|
4611
5349
|
const raw = await Bun.file(filePath).text();
|
|
4612
5350
|
try {
|
|
4613
5351
|
const parsed = parseFrontmatter(raw);
|
|
@@ -4653,8 +5391,8 @@ var init_subagent = __esm(() => {
|
|
|
4653
5391
|
});
|
|
4654
5392
|
|
|
4655
5393
|
// src/validators/claude/command.ts
|
|
4656
|
-
import { existsSync as
|
|
4657
|
-
import { resolve as resolve11, join as
|
|
5394
|
+
import { existsSync as existsSync26, readdirSync as readdirSync13 } from "fs";
|
|
5395
|
+
import { resolve as resolve11, join as join22 } from "path";
|
|
4658
5396
|
var claudeCommandValidator;
|
|
4659
5397
|
var init_command = __esm(() => {
|
|
4660
5398
|
init_frontmatter();
|
|
@@ -4665,10 +5403,10 @@ var init_command = __esm(() => {
|
|
|
4665
5403
|
description: "Validates commands/ (or legacy .claude/commands/) .md files: frontmatter (including rich skill fields), description, body",
|
|
4666
5404
|
detect(dir) {
|
|
4667
5405
|
const commandsDir = resolve11(dir, "commands");
|
|
4668
|
-
if (!
|
|
5406
|
+
if (!existsSync26(commandsDir))
|
|
4669
5407
|
return false;
|
|
4670
5408
|
try {
|
|
4671
|
-
return
|
|
5409
|
+
return readdirSync13(commandsDir).some((f) => f.endsWith(".md"));
|
|
4672
5410
|
} catch {
|
|
4673
5411
|
return false;
|
|
4674
5412
|
}
|
|
@@ -4678,14 +5416,14 @@ var init_command = __esm(() => {
|
|
|
4678
5416
|
const warnings = [];
|
|
4679
5417
|
const passes = [];
|
|
4680
5418
|
const commandsDir = resolve11(dir, "commands");
|
|
4681
|
-
const mdFiles =
|
|
5419
|
+
const mdFiles = readdirSync13(commandsDir).filter((f) => f.endsWith(".md"));
|
|
4682
5420
|
if (mdFiles.length === 0) {
|
|
4683
5421
|
errors.push("commands/ directory has no .md files");
|
|
4684
5422
|
return { errors, warnings, passes };
|
|
4685
5423
|
}
|
|
4686
5424
|
passes.push(`${mdFiles.length} command definition(s) found`);
|
|
4687
5425
|
for (const file of mdFiles) {
|
|
4688
|
-
const filePath =
|
|
5426
|
+
const filePath = join22(commandsDir, file);
|
|
4689
5427
|
const raw = await Bun.file(filePath).text();
|
|
4690
5428
|
try {
|
|
4691
5429
|
const parsed = parseFrontmatter(raw);
|
|
@@ -4714,7 +5452,7 @@ var init_command = __esm(() => {
|
|
|
4714
5452
|
});
|
|
4715
5453
|
|
|
4716
5454
|
// src/validators/claude/memory.ts
|
|
4717
|
-
import { existsSync as
|
|
5455
|
+
import { existsSync as existsSync27 } from "fs";
|
|
4718
5456
|
import { resolve as resolve12 } from "path";
|
|
4719
5457
|
var claudeMemoryValidator;
|
|
4720
5458
|
var init_memory = __esm(() => {
|
|
@@ -4724,7 +5462,7 @@ var init_memory = __esm(() => {
|
|
|
4724
5462
|
name: "Claude CLAUDE.md",
|
|
4725
5463
|
description: "Validates CLAUDE.md: non-empty, length recommendations, @path imports",
|
|
4726
5464
|
detect(dir) {
|
|
4727
|
-
return
|
|
5465
|
+
return existsSync27(resolve12(dir, "CLAUDE.md"));
|
|
4728
5466
|
},
|
|
4729
5467
|
async validate(dir, _opts) {
|
|
4730
5468
|
const errors = [];
|
|
@@ -4749,7 +5487,7 @@ var init_memory = __esm(() => {
|
|
|
4749
5487
|
while ((match = importRegex.exec(raw)) !== null) {
|
|
4750
5488
|
const importPath = match[1];
|
|
4751
5489
|
const resolvedImport = resolve12(dir, importPath);
|
|
4752
|
-
if (
|
|
5490
|
+
if (existsSync27(resolvedImport)) {
|
|
4753
5491
|
passes.push(`@import "${importPath}" exists`);
|
|
4754
5492
|
} else {
|
|
4755
5493
|
warnings.push(`@import "${importPath}" \u2014 file not found at ${resolvedImport}`);
|
|
@@ -4761,7 +5499,7 @@ var init_memory = __esm(() => {
|
|
|
4761
5499
|
});
|
|
4762
5500
|
|
|
4763
5501
|
// src/validators/claude/lsp.ts
|
|
4764
|
-
import { existsSync as
|
|
5502
|
+
import { existsSync as existsSync28 } from "fs";
|
|
4765
5503
|
import { resolve as resolve13 } from "path";
|
|
4766
5504
|
var claudeLspValidator;
|
|
4767
5505
|
var init_lsp = __esm(() => {
|
|
@@ -4771,7 +5509,7 @@ var init_lsp = __esm(() => {
|
|
|
4771
5509
|
name: "Claude LSP Servers",
|
|
4772
5510
|
description: "Validates .lsp.json (or plugin.json lspServers): language server configs with required command + extensionToLanguage; optional transport, env, settings, diagnostics etc. (binaries installed separately)",
|
|
4773
5511
|
detect(dir) {
|
|
4774
|
-
return
|
|
5512
|
+
return existsSync28(resolve13(dir, ".lsp.json")) || existsSync28(resolve13(dir, ".claude-plugin", "plugin.json"));
|
|
4775
5513
|
},
|
|
4776
5514
|
async validate(dir, _opts) {
|
|
4777
5515
|
const errors = [];
|
|
@@ -4779,7 +5517,7 @@ var init_lsp = __esm(() => {
|
|
|
4779
5517
|
const passes = [];
|
|
4780
5518
|
let cfg = null;
|
|
4781
5519
|
const lspPath = resolve13(dir, ".lsp.json");
|
|
4782
|
-
if (
|
|
5520
|
+
if (existsSync28(lspPath)) {
|
|
4783
5521
|
try {
|
|
4784
5522
|
cfg = JSON.parse(await Bun.file(lspPath).text());
|
|
4785
5523
|
passes.push(".lsp.json is valid JSON");
|
|
@@ -4789,7 +5527,7 @@ var init_lsp = __esm(() => {
|
|
|
4789
5527
|
}
|
|
4790
5528
|
} else {
|
|
4791
5529
|
const manifestPath = resolve13(dir, ".claude-plugin", "plugin.json");
|
|
4792
|
-
if (
|
|
5530
|
+
if (existsSync28(manifestPath)) {
|
|
4793
5531
|
try {
|
|
4794
5532
|
const m = JSON.parse(await Bun.file(manifestPath).text());
|
|
4795
5533
|
if (m && m.lspServers && typeof m.lspServers === "object") {
|
|
@@ -4800,7 +5538,7 @@ var init_lsp = __esm(() => {
|
|
|
4800
5538
|
}
|
|
4801
5539
|
}
|
|
4802
5540
|
if (!cfg) {
|
|
4803
|
-
if (!
|
|
5541
|
+
if (!existsSync28(lspPath)) {
|
|
4804
5542
|
return { errors, warnings, passes };
|
|
4805
5543
|
}
|
|
4806
5544
|
}
|
|
@@ -4829,7 +5567,7 @@ var init_lsp = __esm(() => {
|
|
|
4829
5567
|
});
|
|
4830
5568
|
|
|
4831
5569
|
// src/validators/claude/monitors.ts
|
|
4832
|
-
import { existsSync as
|
|
5570
|
+
import { existsSync as existsSync29 } from "fs";
|
|
4833
5571
|
import { resolve as resolve14 } from "path";
|
|
4834
5572
|
var claudeMonitorsValidator;
|
|
4835
5573
|
var init_monitors = __esm(() => {
|
|
@@ -4839,7 +5577,7 @@ var init_monitors = __esm(() => {
|
|
|
4839
5577
|
name: "Claude Monitors (experimental)",
|
|
4840
5578
|
description: "Validates monitors/monitors.json (or experimental.monitors): array of {name, command, description, when?}; commands support ${CLAUDE_PLUGIN_*} subs. Monitors run only in interactive CLI sessions.",
|
|
4841
5579
|
detect(dir) {
|
|
4842
|
-
return
|
|
5580
|
+
return existsSync29(resolve14(dir, "monitors", "monitors.json")) || existsSync29(resolve14(dir, "monitors.json")) || existsSync29(resolve14(dir, ".claude-plugin", "plugin.json"));
|
|
4843
5581
|
},
|
|
4844
5582
|
async validate(dir, _opts) {
|
|
4845
5583
|
const errors = [];
|
|
@@ -4851,7 +5589,7 @@ var init_monitors = __esm(() => {
|
|
|
4851
5589
|
resolve14(dir, "monitors.json")
|
|
4852
5590
|
];
|
|
4853
5591
|
for (const p of candidates) {
|
|
4854
|
-
if (
|
|
5592
|
+
if (existsSync29(p)) {
|
|
4855
5593
|
try {
|
|
4856
5594
|
const parsed = JSON.parse(await Bun.file(p).text());
|
|
4857
5595
|
if (Array.isArray(parsed)) {
|
|
@@ -4867,7 +5605,7 @@ var init_monitors = __esm(() => {
|
|
|
4867
5605
|
}
|
|
4868
5606
|
if (!arr) {
|
|
4869
5607
|
const mp = resolve14(dir, ".claude-plugin", "plugin.json");
|
|
4870
|
-
if (
|
|
5608
|
+
if (existsSync29(mp)) {
|
|
4871
5609
|
try {
|
|
4872
5610
|
const m = JSON.parse(await Bun.file(mp).text());
|
|
4873
5611
|
const exp = m?.experimental;
|
|
@@ -4920,8 +5658,8 @@ var init_monitors = __esm(() => {
|
|
|
4920
5658
|
});
|
|
4921
5659
|
|
|
4922
5660
|
// src/validators/codex/plugin.ts
|
|
4923
|
-
import { existsSync as
|
|
4924
|
-
import { resolve as resolve15, join as
|
|
5661
|
+
import { existsSync as existsSync30, readdirSync as readdirSync14 } from "fs";
|
|
5662
|
+
import { resolve as resolve15, join as join23 } from "path";
|
|
4925
5663
|
var NAME_REGEX3, codexPluginValidator;
|
|
4926
5664
|
var init_plugin2 = __esm(() => {
|
|
4927
5665
|
NAME_REGEX3 = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
@@ -4931,7 +5669,7 @@ var init_plugin2 = __esm(() => {
|
|
|
4931
5669
|
name: "Codex Plugin",
|
|
4932
5670
|
description: "Validates .codex-plugin/plugin.json manifest (requires interface block and skills as directory string per Codex packaging)",
|
|
4933
5671
|
detect(dir) {
|
|
4934
|
-
return
|
|
5672
|
+
return existsSync30(resolve15(dir, ".codex-plugin", "plugin.json"));
|
|
4935
5673
|
},
|
|
4936
5674
|
async validate(dir, _opts) {
|
|
4937
5675
|
const errors = [];
|
|
@@ -5002,12 +5740,21 @@ var init_plugin2 = __esm(() => {
|
|
|
5002
5740
|
} else {
|
|
5003
5741
|
warnings.push('Missing "description" (recommended)');
|
|
5004
5742
|
}
|
|
5743
|
+
if (manifest.keywords !== undefined) {
|
|
5744
|
+
if (Array.isArray(manifest.keywords)) {
|
|
5745
|
+
passes.push(`keywords: [${manifest.keywords.join(", ")}] \u2014 If users mention any of these keywords, your plugin will get triggered in Codex`);
|
|
5746
|
+
} else {
|
|
5747
|
+
errors.push("keywords must be an array of strings");
|
|
5748
|
+
}
|
|
5749
|
+
} else {
|
|
5750
|
+
warnings.push('Missing "keywords" (recommended \u2014 if users mention any of these, your plugin will get triggered in Codex)');
|
|
5751
|
+
}
|
|
5005
5752
|
const skillsDir = resolve15(dir, "skills");
|
|
5006
|
-
if (
|
|
5007
|
-
const entries =
|
|
5753
|
+
if (existsSync30(skillsDir)) {
|
|
5754
|
+
const entries = readdirSync14(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
5008
5755
|
for (const e of entries) {
|
|
5009
|
-
const md =
|
|
5010
|
-
if (
|
|
5756
|
+
const md = join23(skillsDir, e.name, "SKILL.md");
|
|
5757
|
+
if (existsSync30(md)) {
|
|
5011
5758
|
passes.push(`skills/${e.name}/SKILL.md exists`);
|
|
5012
5759
|
} else {
|
|
5013
5760
|
errors.push(`skills/${e.name}/ is missing SKILL.md`);
|
|
@@ -5025,7 +5772,7 @@ var init_plugin2 = __esm(() => {
|
|
|
5025
5772
|
});
|
|
5026
5773
|
|
|
5027
5774
|
// src/validators/codex/marketplace.ts
|
|
5028
|
-
import { existsSync as
|
|
5775
|
+
import { existsSync as existsSync31 } from "fs";
|
|
5029
5776
|
import { resolve as resolve16 } from "path";
|
|
5030
5777
|
var codexMarketplaceValidator;
|
|
5031
5778
|
var init_marketplace2 = __esm(() => {
|
|
@@ -5035,9 +5782,9 @@ var init_marketplace2 = __esm(() => {
|
|
|
5035
5782
|
name: "Codex Plugin Marketplace",
|
|
5036
5783
|
description: "Validates .agents/plugins/marketplace.json (Codex convention: object source + policy blocks)",
|
|
5037
5784
|
detect(dir) {
|
|
5038
|
-
if (
|
|
5785
|
+
if (existsSync31(resolve16(dir, ".agents", "plugins", "marketplace.json")))
|
|
5039
5786
|
return true;
|
|
5040
|
-
if (
|
|
5787
|
+
if (existsSync31(resolve16(dir, ".agents", "plugins", "marketplace.json")))
|
|
5041
5788
|
return true;
|
|
5042
5789
|
return false;
|
|
5043
5790
|
},
|
|
@@ -5046,7 +5793,7 @@ var init_marketplace2 = __esm(() => {
|
|
|
5046
5793
|
const warnings = [];
|
|
5047
5794
|
const passes = [];
|
|
5048
5795
|
const marketplacePath = resolve16(dir, ".agents", "plugins", "marketplace.json");
|
|
5049
|
-
if (!
|
|
5796
|
+
if (!existsSync31(marketplacePath)) {
|
|
5050
5797
|
errors.push("Missing .agents/plugins/marketplace.json");
|
|
5051
5798
|
return { errors, warnings, passes };
|
|
5052
5799
|
}
|
|
@@ -5121,12 +5868,12 @@ var init_marketplace2 = __esm(() => {
|
|
|
5121
5868
|
passes.push(`plugins[${i}].category: "${p.category}"`);
|
|
5122
5869
|
}
|
|
5123
5870
|
}
|
|
5124
|
-
if (
|
|
5871
|
+
if (existsSync31(resolve16(dir, "README.md"))) {
|
|
5125
5872
|
passes.push("README.md exists at marketplace root");
|
|
5126
5873
|
} else {
|
|
5127
5874
|
warnings.push("No README.md at marketplace root \u2014 recommended");
|
|
5128
5875
|
}
|
|
5129
|
-
if (
|
|
5876
|
+
if (existsSync31(resolve16(dir, "LICENSE"))) {
|
|
5130
5877
|
passes.push("LICENSE exists at marketplace root");
|
|
5131
5878
|
} else {
|
|
5132
5879
|
warnings.push("No LICENSE at marketplace root \u2014 recommended");
|
|
@@ -5137,7 +5884,7 @@ var init_marketplace2 = __esm(() => {
|
|
|
5137
5884
|
});
|
|
5138
5885
|
|
|
5139
5886
|
// src/validators/codex/mcp.ts
|
|
5140
|
-
import { existsSync as
|
|
5887
|
+
import { existsSync as existsSync32 } from "fs";
|
|
5141
5888
|
import { resolve as resolve17 } from "path";
|
|
5142
5889
|
var codexMcpValidator;
|
|
5143
5890
|
var init_mcp2 = __esm(() => {
|
|
@@ -5147,7 +5894,7 @@ var init_mcp2 = __esm(() => {
|
|
|
5147
5894
|
name: "Codex MCP Config",
|
|
5148
5895
|
description: "Validates .mcp.json (or inline via plugin.json mcpServers): server entries (stdio: command+args, or url), env, cwd, substitutions per Codex MCP support",
|
|
5149
5896
|
detect(dir) {
|
|
5150
|
-
return
|
|
5897
|
+
return existsSync32(resolve17(dir, ".mcp.json"));
|
|
5151
5898
|
},
|
|
5152
5899
|
async validate(dir, _opts) {
|
|
5153
5900
|
const errors = [];
|
|
@@ -5207,7 +5954,7 @@ var init_mcp2 = __esm(() => {
|
|
|
5207
5954
|
});
|
|
5208
5955
|
|
|
5209
5956
|
// src/validators/codex/skill.ts
|
|
5210
|
-
import { existsSync as
|
|
5957
|
+
import { existsSync as existsSync33 } from "fs";
|
|
5211
5958
|
import { resolve as resolve18 } from "path";
|
|
5212
5959
|
var codexSkillValidator;
|
|
5213
5960
|
var init_skill2 = __esm(() => {
|
|
@@ -5218,7 +5965,7 @@ var init_skill2 = __esm(() => {
|
|
|
5218
5965
|
name: "Codex Skill",
|
|
5219
5966
|
description: "Validates SKILL.md (shared format): frontmatter (name/description), body, supporting files, substitutions. Codex uses the same SKILL.md spec as other providers.",
|
|
5220
5967
|
detect(dir) {
|
|
5221
|
-
return
|
|
5968
|
+
return existsSync33(resolve18(dir, "SKILL.md"));
|
|
5222
5969
|
},
|
|
5223
5970
|
async validate(dir, _opts) {
|
|
5224
5971
|
const loaded = await loadSkill(dir);
|
|
@@ -5236,8 +5983,8 @@ var init_skill2 = __esm(() => {
|
|
|
5236
5983
|
});
|
|
5237
5984
|
|
|
5238
5985
|
// src/validators/cursor/plugin.ts
|
|
5239
|
-
import { existsSync as
|
|
5240
|
-
import { resolve as resolve19, join as
|
|
5986
|
+
import { existsSync as existsSync34, readdirSync as readdirSync16 } from "fs";
|
|
5987
|
+
import { resolve as resolve19, join as join25 } from "path";
|
|
5241
5988
|
var NAME_REGEX4, cursorPluginValidator;
|
|
5242
5989
|
var init_plugin3 = __esm(() => {
|
|
5243
5990
|
NAME_REGEX4 = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
@@ -5247,7 +5994,7 @@ var init_plugin3 = __esm(() => {
|
|
|
5247
5994
|
name: "Cursor Plugin",
|
|
5248
5995
|
description: "Validates .cursor-plugin/plugin.json manifest (skills as directory string; mcpServers support)",
|
|
5249
5996
|
detect(dir) {
|
|
5250
|
-
return
|
|
5997
|
+
return existsSync34(resolve19(dir, ".cursor-plugin", "plugin.json"));
|
|
5251
5998
|
},
|
|
5252
5999
|
async validate(dir, _opts) {
|
|
5253
6000
|
const errors = [];
|
|
@@ -5324,15 +6071,21 @@ var init_plugin3 = __esm(() => {
|
|
|
5324
6071
|
passes.push("homepage present");
|
|
5325
6072
|
if (manifest.repository)
|
|
5326
6073
|
passes.push("repository present");
|
|
5327
|
-
if (
|
|
5328
|
-
|
|
6074
|
+
if (manifest.keywords !== undefined) {
|
|
6075
|
+
if (Array.isArray(manifest.keywords)) {
|
|
6076
|
+
passes.push(`keywords: [${manifest.keywords.join(", ")}] \u2014 If users mention any of these keywords, your plugin will get triggered in Cursor`);
|
|
6077
|
+
} else {
|
|
6078
|
+
errors.push("keywords must be an array of strings");
|
|
6079
|
+
}
|
|
6080
|
+
} else {
|
|
6081
|
+
warnings.push('Missing "keywords" (recommended \u2014 if users mention any of these, your plugin will get triggered in Cursor)');
|
|
5329
6082
|
}
|
|
5330
6083
|
const skillsDir = resolve19(dir, "skills");
|
|
5331
|
-
if (
|
|
5332
|
-
const entries =
|
|
6084
|
+
if (existsSync34(skillsDir)) {
|
|
6085
|
+
const entries = readdirSync16(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
5333
6086
|
for (const e of entries) {
|
|
5334
|
-
const md =
|
|
5335
|
-
if (
|
|
6087
|
+
const md = join25(skillsDir, e.name, "SKILL.md");
|
|
6088
|
+
if (existsSync34(md)) {
|
|
5336
6089
|
passes.push(`skills/${e.name}/SKILL.md exists`);
|
|
5337
6090
|
} else {
|
|
5338
6091
|
errors.push(`skills/${e.name}/ is missing SKILL.md`);
|
|
@@ -5343,7 +6096,7 @@ var init_plugin3 = __esm(() => {
|
|
|
5343
6096
|
const mcpRef = manifest.mcpServers;
|
|
5344
6097
|
if (mcpRef.startsWith("./") || mcpRef.startsWith("../")) {
|
|
5345
6098
|
const mcpPath = resolve19(dir, mcpRef);
|
|
5346
|
-
if (
|
|
6099
|
+
if (existsSync34(mcpPath)) {
|
|
5347
6100
|
passes.push(`mcpServers file exists at ${mcpRef}`);
|
|
5348
6101
|
} else {
|
|
5349
6102
|
warnings.push(`mcpServers path "${mcpRef}" does not exist on disk`);
|
|
@@ -5373,8 +6126,8 @@ var init_plugin3 = __esm(() => {
|
|
|
5373
6126
|
});
|
|
5374
6127
|
|
|
5375
6128
|
// src/validators/cursor/marketplace.ts
|
|
5376
|
-
import { existsSync as
|
|
5377
|
-
import { resolve as resolve20, join as
|
|
6129
|
+
import { existsSync as existsSync35, readdirSync as readdirSync17 } from "fs";
|
|
6130
|
+
import { resolve as resolve20, join as join26 } from "path";
|
|
5378
6131
|
var cursorMarketplaceValidator;
|
|
5379
6132
|
var init_marketplace3 = __esm(() => {
|
|
5380
6133
|
cursorMarketplaceValidator = {
|
|
@@ -5383,18 +6136,18 @@ var init_marketplace3 = __esm(() => {
|
|
|
5383
6136
|
name: "Cursor Plugin Marketplace",
|
|
5384
6137
|
description: "Validates .cursor-plugin/marketplace.json (string sources + metadata.pluginRoot)",
|
|
5385
6138
|
detect(dir) {
|
|
5386
|
-
if (
|
|
6139
|
+
if (existsSync35(resolve20(dir, ".cursor-plugin", "marketplace.json")))
|
|
5387
6140
|
return true;
|
|
5388
6141
|
const pluginsDir = resolve20(dir, "plugins");
|
|
5389
|
-
if (!
|
|
6142
|
+
if (!existsSync35(pluginsDir))
|
|
5390
6143
|
return false;
|
|
5391
6144
|
try {
|
|
5392
|
-
const entries =
|
|
6145
|
+
const entries = readdirSync17(pluginsDir, { withFileTypes: true });
|
|
5393
6146
|
for (const entry of entries) {
|
|
5394
6147
|
if (!entry.isDirectory())
|
|
5395
6148
|
continue;
|
|
5396
|
-
const hasSkills =
|
|
5397
|
-
const hasManifest =
|
|
6149
|
+
const hasSkills = existsSync35(join26(pluginsDir, entry.name, "skills"));
|
|
6150
|
+
const hasManifest = existsSync35(join26(pluginsDir, entry.name, ".cursor-plugin", "plugin.json"));
|
|
5398
6151
|
if (hasSkills || hasManifest)
|
|
5399
6152
|
return true;
|
|
5400
6153
|
}
|
|
@@ -5406,9 +6159,9 @@ var init_marketplace3 = __esm(() => {
|
|
|
5406
6159
|
const warnings = [];
|
|
5407
6160
|
const passes = [];
|
|
5408
6161
|
const cursorMktPath = resolve20(dir, ".cursor-plugin", "marketplace.json");
|
|
5409
|
-
const hasCursorMkt =
|
|
6162
|
+
const hasCursorMkt = existsSync35(cursorMktPath);
|
|
5410
6163
|
const pluginsDir = resolve20(dir, "plugins");
|
|
5411
|
-
const hasPluginsDirLayout =
|
|
6164
|
+
const hasPluginsDirLayout = existsSync35(pluginsDir);
|
|
5412
6165
|
if (!hasCursorMkt && !hasPluginsDirLayout) {
|
|
5413
6166
|
errors.push("Missing .cursor-plugin/marketplace.json or plugins/ directory");
|
|
5414
6167
|
return { errors, warnings, passes };
|
|
@@ -5462,9 +6215,9 @@ var init_marketplace3 = __esm(() => {
|
|
|
5462
6215
|
const src = String(p.source);
|
|
5463
6216
|
passes.push(`plugins[${i}].source: "${src}"`);
|
|
5464
6217
|
const srcDir = resolve20(dir, pluginRoot, src);
|
|
5465
|
-
if (
|
|
5466
|
-
const hasManifest =
|
|
5467
|
-
const hasSkills =
|
|
6218
|
+
if (existsSync35(srcDir)) {
|
|
6219
|
+
const hasManifest = existsSync35(resolve20(srcDir, ".cursor-plugin", "plugin.json"));
|
|
6220
|
+
const hasSkills = existsSync35(resolve20(srcDir, "skills"));
|
|
5468
6221
|
if (hasManifest || hasSkills) {
|
|
5469
6222
|
passes.push(`plugins[${i}]: source exists (${hasManifest ? "manifest" : "skills/"})`);
|
|
5470
6223
|
} else {
|
|
@@ -5475,7 +6228,7 @@ var init_marketplace3 = __esm(() => {
|
|
|
5475
6228
|
}
|
|
5476
6229
|
} else {
|
|
5477
6230
|
const implicitSrc = resolve20(dir, pluginRoot, p.name || "");
|
|
5478
|
-
if (p.name &&
|
|
6231
|
+
if (p.name && existsSync35(implicitSrc)) {
|
|
5479
6232
|
passes.push(`plugins[${i}]: implicit source via name under ${pluginRoot}`);
|
|
5480
6233
|
} else {
|
|
5481
6234
|
warnings.push(`plugins[${i}]: missing "source" (and no implicit dir)`);
|
|
@@ -5490,12 +6243,12 @@ var init_marketplace3 = __esm(() => {
|
|
|
5490
6243
|
passes.push(`plugins[${i}].homepage present`);
|
|
5491
6244
|
}
|
|
5492
6245
|
}
|
|
5493
|
-
if (
|
|
6246
|
+
if (existsSync35(resolve20(dir, "README.md"))) {
|
|
5494
6247
|
passes.push("README.md exists at marketplace root");
|
|
5495
6248
|
} else {
|
|
5496
6249
|
warnings.push("No README.md at marketplace root \u2014 recommended for discoverability");
|
|
5497
6250
|
}
|
|
5498
|
-
if (
|
|
6251
|
+
if (existsSync35(resolve20(dir, "LICENSE"))) {
|
|
5499
6252
|
passes.push("LICENSE exists at marketplace root");
|
|
5500
6253
|
} else {
|
|
5501
6254
|
warnings.push("No LICENSE at marketplace root \u2014 recommended");
|
|
@@ -5504,21 +6257,21 @@ var init_marketplace3 = __esm(() => {
|
|
|
5504
6257
|
}
|
|
5505
6258
|
if (hasPluginsDirLayout) {
|
|
5506
6259
|
passes.push("plugins/ directory exists");
|
|
5507
|
-
const pluginEntries =
|
|
6260
|
+
const pluginEntries = readdirSync17(pluginsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
5508
6261
|
if (pluginEntries.length === 0) {
|
|
5509
6262
|
errors.push("plugins/ directory is empty \u2014 expected at least one plugin");
|
|
5510
6263
|
return { errors, warnings, passes };
|
|
5511
6264
|
}
|
|
5512
6265
|
passes.push(`${pluginEntries.length} plugin(s) found`);
|
|
5513
|
-
if (
|
|
6266
|
+
if (existsSync35(resolve20(dir, "README.md"))) {
|
|
5514
6267
|
passes.push("README.md exists at marketplace root");
|
|
5515
6268
|
} else {
|
|
5516
6269
|
warnings.push("No README.md at marketplace root \u2014 recommended");
|
|
5517
6270
|
}
|
|
5518
6271
|
for (const plugin of pluginEntries) {
|
|
5519
|
-
const pluginPath =
|
|
5520
|
-
const hasSkills =
|
|
5521
|
-
const hasManifest =
|
|
6272
|
+
const pluginPath = join26(pluginsDir, plugin.name);
|
|
6273
|
+
const hasSkills = existsSync35(join26(pluginPath, "skills"));
|
|
6274
|
+
const hasManifest = existsSync35(join26(pluginPath, ".cursor-plugin", "plugin.json"));
|
|
5522
6275
|
if (hasManifest || hasSkills) {
|
|
5523
6276
|
passes.push(`Plugin "${plugin.name}" has ${hasManifest ? "manifest" : "skills/"}`);
|
|
5524
6277
|
}
|
|
@@ -5531,7 +6284,7 @@ var init_marketplace3 = __esm(() => {
|
|
|
5531
6284
|
});
|
|
5532
6285
|
|
|
5533
6286
|
// src/validators/cursor/mcp.ts
|
|
5534
|
-
import { existsSync as
|
|
6287
|
+
import { existsSync as existsSync36 } from "fs";
|
|
5535
6288
|
import { resolve as resolve21 } from "path";
|
|
5536
6289
|
var cursorMcpValidator;
|
|
5537
6290
|
var init_mcp3 = __esm(() => {
|
|
@@ -5541,7 +6294,7 @@ var init_mcp3 = __esm(() => {
|
|
|
5541
6294
|
name: "Cursor MCP Config",
|
|
5542
6295
|
description: "Validates mcp.json (Cursor uses no leading dot; supports mcpServers wrapper or direct server map)",
|
|
5543
6296
|
detect(dir) {
|
|
5544
|
-
return
|
|
6297
|
+
return existsSync36(resolve21(dir, "mcp.json"));
|
|
5545
6298
|
},
|
|
5546
6299
|
async validate(dir, _opts) {
|
|
5547
6300
|
const errors = [];
|
|
@@ -5607,7 +6360,7 @@ var init_mcp3 = __esm(() => {
|
|
|
5607
6360
|
});
|
|
5608
6361
|
|
|
5609
6362
|
// src/validators/cursor/skill.ts
|
|
5610
|
-
import { existsSync as
|
|
6363
|
+
import { existsSync as existsSync37 } from "fs";
|
|
5611
6364
|
import { resolve as resolve22 } from "path";
|
|
5612
6365
|
var cursorSkillValidator;
|
|
5613
6366
|
var init_skill3 = __esm(() => {
|
|
@@ -5618,7 +6371,7 @@ var init_skill3 = __esm(() => {
|
|
|
5618
6371
|
name: "Cursor Skill",
|
|
5619
6372
|
description: "Validates SKILL.md (shared format): frontmatter (name/description), body, supporting files, substitutions. Cursor uses the same SKILL.md spec as other providers.",
|
|
5620
6373
|
detect(dir) {
|
|
5621
|
-
return
|
|
6374
|
+
return existsSync37(resolve22(dir, "SKILL.md"));
|
|
5622
6375
|
},
|
|
5623
6376
|
async validate(dir, _opts) {
|
|
5624
6377
|
const loaded = await loadSkill(dir);
|
|
@@ -5636,7 +6389,7 @@ var init_skill3 = __esm(() => {
|
|
|
5636
6389
|
});
|
|
5637
6390
|
|
|
5638
6391
|
// src/validators/copilot/plugin.ts
|
|
5639
|
-
import { existsSync as
|
|
6392
|
+
import { existsSync as existsSync38 } from "fs";
|
|
5640
6393
|
import { resolve as resolve23 } from "path";
|
|
5641
6394
|
var NAME_REGEX5, copilotPluginValidator;
|
|
5642
6395
|
var init_plugin4 = __esm(() => {
|
|
@@ -5647,7 +6400,7 @@ var init_plugin4 = __esm(() => {
|
|
|
5647
6400
|
name: "Copilot Plugin",
|
|
5648
6401
|
description: "Validates .github/plugin/plugin.json (skills as array of paths, mcpServers support)",
|
|
5649
6402
|
detect(dir) {
|
|
5650
|
-
return
|
|
6403
|
+
return existsSync38(resolve23(dir, ".github", "plugin", "plugin.json"));
|
|
5651
6404
|
},
|
|
5652
6405
|
async validate(dir, _opts) {
|
|
5653
6406
|
const errors = [];
|
|
@@ -5690,9 +6443,9 @@ var init_plugin4 = __esm(() => {
|
|
|
5690
6443
|
}
|
|
5691
6444
|
const skillDir = resolve23(dir, p);
|
|
5692
6445
|
const skillMd = resolve23(skillDir, "SKILL.md");
|
|
5693
|
-
if (
|
|
6446
|
+
if (existsSync38(skillMd)) {
|
|
5694
6447
|
passes.push(`skills[${i}]: ${p}/SKILL.md exists`);
|
|
5695
|
-
} else if (
|
|
6448
|
+
} else if (existsSync38(skillDir)) {
|
|
5696
6449
|
warnings.push(`skills[${i}]: directory exists but no SKILL.md inside`);
|
|
5697
6450
|
} else {
|
|
5698
6451
|
warnings.push(`skills[${i}]: path "${p}" does not exist`);
|
|
@@ -5704,7 +6457,7 @@ var init_plugin4 = __esm(() => {
|
|
|
5704
6457
|
passes.push(`mcpServers: "${manifest.mcpServers}"`);
|
|
5705
6458
|
const mcpRef = String(manifest.mcpServers);
|
|
5706
6459
|
const mcpPath = resolve23(dir, mcpRef);
|
|
5707
|
-
if (
|
|
6460
|
+
if (existsSync38(mcpPath)) {
|
|
5708
6461
|
passes.push(`mcpServers file exists at ${mcpRef}`);
|
|
5709
6462
|
} else {
|
|
5710
6463
|
warnings.push(`mcpServers path "${mcpRef}" does not exist on disk`);
|
|
@@ -5733,8 +6486,14 @@ var init_plugin4 = __esm(() => {
|
|
|
5733
6486
|
passes.push("homepage present");
|
|
5734
6487
|
if (manifest.repository)
|
|
5735
6488
|
passes.push("repository present");
|
|
5736
|
-
if (
|
|
5737
|
-
|
|
6489
|
+
if (manifest.keywords !== undefined) {
|
|
6490
|
+
if (Array.isArray(manifest.keywords)) {
|
|
6491
|
+
passes.push(`keywords: [${manifest.keywords.join(", ")}] \u2014 If users mention any of these keywords, your plugin will get triggered in Copilot CLI`);
|
|
6492
|
+
} else {
|
|
6493
|
+
errors.push("keywords must be an array of strings");
|
|
6494
|
+
}
|
|
6495
|
+
} else {
|
|
6496
|
+
warnings.push('Missing "keywords" (recommended \u2014 if users mention any of these, your plugin will get triggered in Copilot CLI)');
|
|
5738
6497
|
}
|
|
5739
6498
|
const known = new Set([
|
|
5740
6499
|
"name",
|
|
@@ -5758,7 +6517,7 @@ var init_plugin4 = __esm(() => {
|
|
|
5758
6517
|
});
|
|
5759
6518
|
|
|
5760
6519
|
// src/validators/copilot/marketplace.ts
|
|
5761
|
-
import { existsSync as
|
|
6520
|
+
import { existsSync as existsSync39 } from "fs";
|
|
5762
6521
|
import { resolve as resolve24 } from "path";
|
|
5763
6522
|
var copilotMarketplaceValidator;
|
|
5764
6523
|
var init_marketplace4 = __esm(() => {
|
|
@@ -5768,7 +6527,7 @@ var init_marketplace4 = __esm(() => {
|
|
|
5768
6527
|
name: "Copilot Plugin Marketplace",
|
|
5769
6528
|
description: "Validates .github/plugin/marketplace.json (string sources)",
|
|
5770
6529
|
detect(dir) {
|
|
5771
|
-
return
|
|
6530
|
+
return existsSync39(resolve24(dir, ".github", "plugin", "marketplace.json"));
|
|
5772
6531
|
},
|
|
5773
6532
|
async validate(dir, _opts) {
|
|
5774
6533
|
const errors = [];
|
|
@@ -5818,9 +6577,9 @@ var init_marketplace4 = __esm(() => {
|
|
|
5818
6577
|
const src = String(p.source);
|
|
5819
6578
|
passes.push(`plugins[${i}].source: "${src}"`);
|
|
5820
6579
|
const srcDir = resolve24(dir, src);
|
|
5821
|
-
if (
|
|
5822
|
-
const hasManifest =
|
|
5823
|
-
const hasSkills =
|
|
6580
|
+
if (existsSync39(srcDir)) {
|
|
6581
|
+
const hasManifest = existsSync39(resolve24(srcDir, ".github", "plugin", "plugin.json"));
|
|
6582
|
+
const hasSkills = existsSync39(resolve24(srcDir, "skills"));
|
|
5824
6583
|
if (hasManifest || hasSkills) {
|
|
5825
6584
|
passes.push(`plugins[${i}]: source exists (${hasManifest ? "manifest" : "skills/"})`);
|
|
5826
6585
|
} else {
|
|
@@ -5837,12 +6596,12 @@ var init_marketplace4 = __esm(() => {
|
|
|
5837
6596
|
if (p.version)
|
|
5838
6597
|
passes.push(`plugins[${i}].version: "${p.version}"`);
|
|
5839
6598
|
}
|
|
5840
|
-
if (
|
|
6599
|
+
if (existsSync39(resolve24(dir, "README.md"))) {
|
|
5841
6600
|
passes.push("README.md exists at marketplace root");
|
|
5842
6601
|
} else {
|
|
5843
6602
|
warnings.push("No README.md at marketplace root \u2014 recommended");
|
|
5844
6603
|
}
|
|
5845
|
-
if (
|
|
6604
|
+
if (existsSync39(resolve24(dir, "LICENSE"))) {
|
|
5846
6605
|
passes.push("LICENSE exists at marketplace root");
|
|
5847
6606
|
} else {
|
|
5848
6607
|
warnings.push("No LICENSE at marketplace root \u2014 recommended");
|
|
@@ -5853,7 +6612,7 @@ var init_marketplace4 = __esm(() => {
|
|
|
5853
6612
|
});
|
|
5854
6613
|
|
|
5855
6614
|
// src/validators/copilot/mcp.ts
|
|
5856
|
-
import { existsSync as
|
|
6615
|
+
import { existsSync as existsSync40 } from "fs";
|
|
5857
6616
|
import { resolve as resolve25 } from "path";
|
|
5858
6617
|
var copilotMcpValidator;
|
|
5859
6618
|
var init_mcp4 = __esm(() => {
|
|
@@ -5863,7 +6622,7 @@ var init_mcp4 = __esm(() => {
|
|
|
5863
6622
|
name: "Copilot MCP Config",
|
|
5864
6623
|
description: "Validates .mcp.json (referenced via mcpServers in manifest). Supports stdio and http servers.",
|
|
5865
6624
|
detect(dir) {
|
|
5866
|
-
return
|
|
6625
|
+
return existsSync40(resolve25(dir, ".mcp.json"));
|
|
5867
6626
|
},
|
|
5868
6627
|
async validate(dir, _opts) {
|
|
5869
6628
|
const errors = [];
|
|
@@ -5929,7 +6688,7 @@ var init_mcp4 = __esm(() => {
|
|
|
5929
6688
|
});
|
|
5930
6689
|
|
|
5931
6690
|
// src/validators/copilot/skill.ts
|
|
5932
|
-
import { existsSync as
|
|
6691
|
+
import { existsSync as existsSync41 } from "fs";
|
|
5933
6692
|
import { resolve as resolve26 } from "path";
|
|
5934
6693
|
var copilotSkillValidator;
|
|
5935
6694
|
var init_skill4 = __esm(() => {
|
|
@@ -5940,7 +6699,7 @@ var init_skill4 = __esm(() => {
|
|
|
5940
6699
|
name: "Copilot Skill",
|
|
5941
6700
|
description: "Validates SKILL.md (shared format). Copilot supports skills referenced via array paths in the manifest.",
|
|
5942
6701
|
detect(dir) {
|
|
5943
|
-
return
|
|
6702
|
+
return existsSync41(resolve26(dir, "SKILL.md"));
|
|
5944
6703
|
},
|
|
5945
6704
|
async validate(dir, _opts) {
|
|
5946
6705
|
const loaded = await loadSkill(dir);
|
|
@@ -6108,7 +6867,7 @@ var init_validators = __esm(() => {
|
|
|
6108
6867
|
// src/core/remote.ts
|
|
6109
6868
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
6110
6869
|
import { mkdtempSync, rmSync } from "fs";
|
|
6111
|
-
import { join as
|
|
6870
|
+
import { join as join28 } from "path";
|
|
6112
6871
|
import { tmpdir } from "os";
|
|
6113
6872
|
function parseRemoteUrl(input) {
|
|
6114
6873
|
if (input.startsWith(".") || input.startsWith("/") || input.startsWith("~")) {
|
|
@@ -6145,7 +6904,7 @@ function isGhAvailable() {
|
|
|
6145
6904
|
return ghAvailable;
|
|
6146
6905
|
}
|
|
6147
6906
|
async function cloneToTemp(parsed) {
|
|
6148
|
-
const tmpDir = mkdtempSync(
|
|
6907
|
+
const tmpDir = mkdtempSync(join28(tmpdir(), "dora-"));
|
|
6149
6908
|
const cleanup = () => {
|
|
6150
6909
|
try {
|
|
6151
6910
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
@@ -6193,19 +6952,19 @@ var exports_validate_top = {};
|
|
|
6193
6952
|
__export(exports_validate_top, {
|
|
6194
6953
|
default: () => validate_top_default
|
|
6195
6954
|
});
|
|
6196
|
-
import { existsSync as
|
|
6955
|
+
import { existsSync as existsSync43 } from "fs";
|
|
6197
6956
|
import { resolve as resolve27 } from "path";
|
|
6198
|
-
var
|
|
6957
|
+
var import_picocolors17, validate_top_default;
|
|
6199
6958
|
var init_validate_top = __esm(() => {
|
|
6200
6959
|
init_dist();
|
|
6201
6960
|
init_out();
|
|
6202
6961
|
init_validators();
|
|
6203
6962
|
init_remote();
|
|
6204
|
-
|
|
6963
|
+
import_picocolors17 = __toESM(require_picocolors(), 1);
|
|
6205
6964
|
validate_top_default = defineCommand({
|
|
6206
6965
|
meta: {
|
|
6207
6966
|
name: "validate",
|
|
6208
|
-
description: "Auto-detect project type and run matching validators. Accepts a local path or a Git URL (e.g. https://github.com/owner/repo)"
|
|
6967
|
+
description: "Auto-detect project type and run matching validators. Accepts a local path or a Git URL (e.g. https://github.com/owner/repo). Use --for <provider>:plugin to see keyword trigger messages."
|
|
6209
6968
|
},
|
|
6210
6969
|
args: {
|
|
6211
6970
|
path: {
|
|
@@ -6241,7 +7000,7 @@ var init_validate_top = __esm(() => {
|
|
|
6241
7000
|
let cleanup;
|
|
6242
7001
|
if (remote) {
|
|
6243
7002
|
ui.info(`
|
|
6244
|
-
Cloning ${
|
|
7003
|
+
Cloning ${import_picocolors17.default.dim(args.path)}...`);
|
|
6245
7004
|
try {
|
|
6246
7005
|
const result = await cloneToTemp(remote);
|
|
6247
7006
|
fullPath = remote.subpath ? resolve27(result.dir, remote.subpath) : result.dir;
|
|
@@ -6251,14 +7010,14 @@ var init_validate_top = __esm(() => {
|
|
|
6251
7010
|
ui.fail(msg);
|
|
6252
7011
|
process.exit(1);
|
|
6253
7012
|
}
|
|
6254
|
-
if (!
|
|
7013
|
+
if (!existsSync43(fullPath)) {
|
|
6255
7014
|
cleanup();
|
|
6256
7015
|
ui.fail(`Subdirectory not found in repo: ${remote.subpath}`);
|
|
6257
7016
|
process.exit(1);
|
|
6258
7017
|
}
|
|
6259
7018
|
} else {
|
|
6260
7019
|
fullPath = resolve27(args.path);
|
|
6261
|
-
if (!
|
|
7020
|
+
if (!existsSync43(fullPath)) {
|
|
6262
7021
|
ui.fail(`Path not found: ${args.path}
|
|
6263
7022
|
|
|
6264
7023
|
Check that the path is correct and the directory exists.`);
|
|
@@ -6289,13 +7048,13 @@ Check that the path is correct and the directory exists.`);
|
|
|
6289
7048
|
` + `Available providers:
|
|
6290
7049
|
` + providers.map((p) => {
|
|
6291
7050
|
const pvs = validators.filter((v) => v.provider === p);
|
|
6292
|
-
return ` ${
|
|
6293
|
-
` + pvs.map((v) => ` \u2022 ${
|
|
7051
|
+
return ` ${import_picocolors17.default.bold(p)}
|
|
7052
|
+
` + pvs.map((v) => ` \u2022 ${import_picocolors17.default.dim(v.id)} \u2014 ${v.description}`).join(`
|
|
6294
7053
|
`);
|
|
6295
7054
|
}).join(`
|
|
6296
7055
|
`) + `
|
|
6297
7056
|
|
|
6298
|
-
Use ${
|
|
7057
|
+
Use ${import_picocolors17.default.dim("--for <provider>")} or ${import_picocolors17.default.dim("--for <provider:type>")} to target explicitly.`);
|
|
6299
7058
|
process.exit(1);
|
|
6300
7059
|
}
|
|
6301
7060
|
const allResults = [];
|
|
@@ -6316,7 +7075,7 @@ Use ${import_picocolors15.default.dim("--for <provider>")} or ${import_picocolor
|
|
|
6316
7075
|
} else {
|
|
6317
7076
|
for (const { id, name, result } of allResults) {
|
|
6318
7077
|
ui.write(`
|
|
6319
|
-
${
|
|
7078
|
+
${import_picocolors17.default.bold("dora validate")} \u2014 ${import_picocolors17.default.white(name)} ${import_picocolors17.default.dim(`(${id})`)}
|
|
6320
7079
|
`);
|
|
6321
7080
|
ui.info(` Path: ${args.path}
|
|
6322
7081
|
`);
|
|
@@ -6331,7 +7090,7 @@ Use ${import_picocolors15.default.dim("--for <provider>")} or ${import_picocolor
|
|
|
6331
7090
|
}
|
|
6332
7091
|
if (result.errors.length === 0 && result.warnings.length === 0) {
|
|
6333
7092
|
ui.write(`
|
|
6334
|
-
${
|
|
7093
|
+
${import_picocolors17.default.green("\u2713")} ${import_picocolors17.default.white("All checks passed.")}
|
|
6335
7094
|
`);
|
|
6336
7095
|
} else {
|
|
6337
7096
|
ui.info(`
|
|
@@ -6353,16 +7112,16 @@ var exports_init2 = {};
|
|
|
6353
7112
|
__export(exports_init2, {
|
|
6354
7113
|
default: () => init_default2
|
|
6355
7114
|
});
|
|
6356
|
-
import { basename as basename6, join as
|
|
7115
|
+
import { basename as basename6, join as join29 } from "path";
|
|
6357
7116
|
var {spawnSync: spawnSync5 } = globalThis.Bun;
|
|
6358
|
-
var
|
|
7117
|
+
var import_picocolors18, init_default2;
|
|
6359
7118
|
var init_init2 = __esm(() => {
|
|
6360
7119
|
init_dist();
|
|
6361
7120
|
init_out();
|
|
6362
7121
|
init_journal_config();
|
|
6363
7122
|
init_journal_remote();
|
|
6364
7123
|
init_prompt();
|
|
6365
|
-
|
|
7124
|
+
import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
6366
7125
|
init_default2 = defineCommand({
|
|
6367
7126
|
meta: {
|
|
6368
7127
|
name: "init",
|
|
@@ -6389,17 +7148,17 @@ var init_init2 = __esm(() => {
|
|
|
6389
7148
|
ui.heading("dora init \u2014 Set up doraval, your journal, and the coding agent dora should use on the fly");
|
|
6390
7149
|
const ghCheck = ensureGhCli();
|
|
6391
7150
|
if (!ghCheck.ok) {
|
|
6392
|
-
ui.write(` ${
|
|
7151
|
+
ui.write(` ${import_picocolors18.default.red("\u2717")} ${import_picocolors18.default.white("The GitHub CLI (")}${import_picocolors18.default.bold("gh")}${import_picocolors18.default.white(") is not installed.")}
|
|
6393
7152
|
`);
|
|
6394
|
-
ui.info(` doraval uses ${
|
|
7153
|
+
ui.info(` doraval uses ${import_picocolors18.default.bold("gh")} to fetch and sync journal files with GitHub.
|
|
6395
7154
|
`);
|
|
6396
7155
|
ui.info(` Install it:
|
|
6397
7156
|
`);
|
|
6398
|
-
ui.info(` macOS: ${
|
|
6399
|
-
ui.info(` Linux: ${
|
|
6400
|
-
ui.info(` Windows: ${
|
|
7157
|
+
ui.info(` macOS: ${import_picocolors18.default.dim("brew install gh")}`);
|
|
7158
|
+
ui.info(` Linux: ${import_picocolors18.default.dim("https://github.com/cli/cli/blob/trunk/docs/install_linux.md")}`);
|
|
7159
|
+
ui.info(` Windows: ${import_picocolors18.default.dim("winget install --id GitHub.cli")}
|
|
6401
7160
|
`);
|
|
6402
|
-
ui.info(` Then authenticate: ${
|
|
7161
|
+
ui.info(` Then authenticate: ${import_picocolors18.default.dim("gh auth login")}
|
|
6403
7162
|
`);
|
|
6404
7163
|
process.exit(1);
|
|
6405
7164
|
}
|
|
@@ -6412,28 +7171,28 @@ var init_init2 = __esm(() => {
|
|
|
6412
7171
|
if (gitOwner) {
|
|
6413
7172
|
defaultRepo = `${gitOwner}/${gitOwner}.md`;
|
|
6414
7173
|
if (ghLogin && ghLogin !== gitOwner) {
|
|
6415
|
-
sourceNote = ` ${
|
|
7174
|
+
sourceNote = ` ${import_picocolors18.default.dim("(from git remote; your active gh account is " + ghLogin + ")")}
|
|
6416
7175
|
`;
|
|
6417
7176
|
} else {
|
|
6418
|
-
sourceNote = ` ${
|
|
7177
|
+
sourceNote = ` ${import_picocolors18.default.dim("(from git remote)")}
|
|
6419
7178
|
`;
|
|
6420
7179
|
}
|
|
6421
7180
|
} else if (ghLogin) {
|
|
6422
7181
|
defaultRepo = `${ghLogin}/${ghLogin}.md`;
|
|
6423
|
-
sourceNote = ` ${
|
|
7182
|
+
sourceNote = ` ${import_picocolors18.default.dim("(from your active gh account)")}
|
|
6424
7183
|
`;
|
|
6425
7184
|
} else {
|
|
6426
|
-
ui.warn(`Not logged in to GitHub. Run ${
|
|
7185
|
+
ui.warn(`Not logged in to GitHub. Run ${import_picocolors18.default.dim("gh auth login")} first.
|
|
6427
7186
|
`);
|
|
6428
7187
|
process.exit(1);
|
|
6429
7188
|
}
|
|
6430
7189
|
const existingConfig = await readConfig();
|
|
6431
7190
|
if (existingConfig?.journal.repo) {
|
|
6432
7191
|
defaultRepo = existingConfig.journal.repo;
|
|
6433
|
-
sourceNote = ` ${
|
|
7192
|
+
sourceNote = ` ${import_picocolors18.default.dim("(from your previous journal setup)")}
|
|
6434
7193
|
`;
|
|
6435
7194
|
}
|
|
6436
|
-
ui.info(` Journal repo ${
|
|
7195
|
+
ui.info(` Journal repo ${import_picocolors18.default.dim("(owner/name)")}`);
|
|
6437
7196
|
if (sourceNote)
|
|
6438
7197
|
ui.write(sourceNote);
|
|
6439
7198
|
repo = prompt(" >", defaultRepo);
|
|
@@ -6445,11 +7204,11 @@ var init_init2 = __esm(() => {
|
|
|
6445
7204
|
}
|
|
6446
7205
|
project = sanitizeProjectName(project);
|
|
6447
7206
|
if (!repoExists(repo)) {
|
|
6448
|
-
ui.write(` ${
|
|
7207
|
+
ui.write(` ${import_picocolors18.default.red("\u2717")} ${import_picocolors18.default.white("Repository")} ${import_picocolors18.default.bold(repo)} ${import_picocolors18.default.white("not found on GitHub.")}
|
|
6449
7208
|
`);
|
|
6450
7209
|
ui.info(` Create it first:
|
|
6451
7210
|
`);
|
|
6452
|
-
ui.info(` ${
|
|
7211
|
+
ui.info(` ${import_picocolors18.default.dim(`gh repo create ${repo} --private --description "Personal journal for agent decisions"`)}
|
|
6453
7212
|
`);
|
|
6454
7213
|
process.exit(1);
|
|
6455
7214
|
}
|
|
@@ -6457,16 +7216,16 @@ var init_init2 = __esm(() => {
|
|
|
6457
7216
|
const alreadyRegistered = existing?.journal.projects[project];
|
|
6458
7217
|
const isRefresh = alreadyRegistered && args.refresh;
|
|
6459
7218
|
if (alreadyRegistered && !isRefresh) {
|
|
6460
|
-
ui.write(` ${
|
|
7219
|
+
ui.write(` ${import_picocolors18.default.yellow("\u26A0")} ${import_picocolors18.default.white("Project")} ${import_picocolors18.default.bold(project)} ${import_picocolors18.default.white("is already registered.")}
|
|
6461
7220
|
`);
|
|
6462
7221
|
ui.info(` Repo: ${existing.journal.repo}
|
|
6463
7222
|
`);
|
|
6464
|
-
ui.info(` To refresh journal files, use ${
|
|
7223
|
+
ui.info(` To refresh journal files, use ${import_picocolors18.default.dim("dora journal update")} (or ${import_picocolors18.default.dim("dora init --refresh")}).
|
|
6465
7224
|
`);
|
|
6466
7225
|
}
|
|
6467
7226
|
const journalsDir = getJournalsDir();
|
|
6468
7227
|
const remotePath = `projects/${project}.md`;
|
|
6469
|
-
const localPath =
|
|
7228
|
+
const localPath = join29(journalsDir, `${project}.md`);
|
|
6470
7229
|
const effectiveRepo = isRefresh && !args.repo ? existing.journal.repo : repo;
|
|
6471
7230
|
const config = existing ?? {
|
|
6472
7231
|
journal: { repo: effectiveRepo, projects: {} }
|
|
@@ -6477,9 +7236,9 @@ var init_init2 = __esm(() => {
|
|
|
6477
7236
|
local_path: localPath
|
|
6478
7237
|
};
|
|
6479
7238
|
ensureDoravalDirs();
|
|
6480
|
-
ui.write(` ${
|
|
7239
|
+
ui.write(` ${import_picocolors18.default.dim(import_picocolors18.default.gray("Fetching journal files from"))} ${import_picocolors18.default.gray(effectiveRepo)}${import_picocolors18.default.dim(import_picocolors18.default.gray("..."))}
|
|
6481
7240
|
`);
|
|
6482
|
-
const globalDest =
|
|
7241
|
+
const globalDest = join29(journalsDir, "global.md");
|
|
6483
7242
|
const refreshGlobalRes = await refreshLocalJournalFile(effectiveRepo, "global.md", globalDest);
|
|
6484
7243
|
let wroteGlobal;
|
|
6485
7244
|
if (!refreshGlobalRes.ok) {
|
|
@@ -6496,7 +7255,7 @@ var init_init2 = __esm(() => {
|
|
|
6496
7255
|
if (wroteGlobal) {
|
|
6497
7256
|
ui.success("global.md");
|
|
6498
7257
|
} else {
|
|
6499
|
-
ui.write(` ${
|
|
7258
|
+
ui.write(` ${import_picocolors18.default.dim("\xB7")} global.md ${import_picocolors18.default.dim("(not found \u2014 will be created on first sync)")}`);
|
|
6500
7259
|
await Bun.write(globalDest, `# Global Journal
|
|
6501
7260
|
|
|
6502
7261
|
Cross-project principles.
|
|
@@ -6518,7 +7277,7 @@ Cross-project principles.
|
|
|
6518
7277
|
if (wroteProject) {
|
|
6519
7278
|
ui.success(remotePath);
|
|
6520
7279
|
} else {
|
|
6521
|
-
ui.write(` ${
|
|
7280
|
+
ui.write(` ${import_picocolors18.default.dim("\xB7")} ${remotePath} ${import_picocolors18.default.dim("(not found \u2014 will be created on first sync)")}`);
|
|
6522
7281
|
await Bun.write(localPath, `# ${project} Journal
|
|
6523
7282
|
|
|
6524
7283
|
Project-specific decisions.
|
|
@@ -6526,13 +7285,13 @@ Project-specific decisions.
|
|
|
6526
7285
|
}
|
|
6527
7286
|
await writeConfig(config);
|
|
6528
7287
|
ui.write(`
|
|
6529
|
-
${
|
|
7288
|
+
${import_picocolors18.default.green("\u2713")} ${import_picocolors18.default.white("Journal ready for project")} ${import_picocolors18.default.bold(import_picocolors18.default.white(project))}.
|
|
6530
7289
|
`);
|
|
6531
7290
|
const existingAgent = (await readConfig())?.agent;
|
|
6532
7291
|
if (existingAgent?.command) {
|
|
6533
|
-
ui.write(` ${
|
|
7292
|
+
ui.write(` ${import_picocolors18.default.bold(import_picocolors18.default.white("Coding agent (already configured)"))}
|
|
6534
7293
|
`);
|
|
6535
|
-
ui.write(` Current: ${
|
|
7294
|
+
ui.write(` Current: ${import_picocolors18.default.dim(import_picocolors18.default.gray(existingAgent.command))} template: ${import_picocolors18.default.dim(import_picocolors18.default.gray(existingAgent.prompt_template || "(default)"))}
|
|
6536
7295
|
`);
|
|
6537
7296
|
const change = prompt(" Reconfigure / change the coding agent for on-the-fly enrichment? (y/N)", "n");
|
|
6538
7297
|
if (!/^y/i.test(String(change))) {
|
|
@@ -6542,16 +7301,16 @@ Project-specific decisions.
|
|
|
6542
7301
|
if (existingAgent)
|
|
6543
7302
|
cfg.agent = existingAgent;
|
|
6544
7303
|
await writeConfig(cfg);
|
|
6545
|
-
ui.write(` ${
|
|
7304
|
+
ui.write(` ${import_picocolors18.default.green("\u2713")} ${import_picocolors18.default.white("Try:")} ${import_picocolors18.default.dim(import_picocolors18.default.gray('dora journal add "short decision"'))}
|
|
6546
7305
|
`);
|
|
6547
7306
|
process.exit(0);
|
|
6548
7307
|
return;
|
|
6549
7308
|
}
|
|
6550
7309
|
ui.blank();
|
|
6551
7310
|
} else {
|
|
6552
|
-
ui.write(` ${
|
|
7311
|
+
ui.write(` ${import_picocolors18.default.bold(import_picocolors18.default.white("Coding agent for journal add"))}
|
|
6553
7312
|
`);
|
|
6554
|
-
ui.info(` When configured, ${
|
|
7313
|
+
ui.info(` When configured, ${import_picocolors18.default.dim(import_picocolors18.default.gray('dora journal add ".."'))} will use your agent to enrich entries with tags and rationale automatically.
|
|
6555
7314
|
`);
|
|
6556
7315
|
}
|
|
6557
7316
|
const common = [
|
|
@@ -6570,7 +7329,7 @@ Project-specific decisions.
|
|
|
6570
7329
|
}
|
|
6571
7330
|
}
|
|
6572
7331
|
let agentCmd = detected || "claude";
|
|
6573
|
-
ui.write(` Detected / default agent command: ${
|
|
7332
|
+
ui.write(` Detected / default agent command: ${import_picocolors18.default.dim(import_picocolors18.default.gray(agentCmd))}`);
|
|
6574
7333
|
agentCmd = prompt(" Agent command (the binary you run for prompts)", agentCmd);
|
|
6575
7334
|
let template = detected ? common.find((c) => c.name === detected)?.template || '-p "{{prompt}}" --output-format json' : '-p "{{prompt}}" --output-format json';
|
|
6576
7335
|
ui.info(` Prompt template (use {{prompt}} placeholder):`);
|
|
@@ -6582,11 +7341,11 @@ Project-specific decisions.
|
|
|
6582
7341
|
};
|
|
6583
7342
|
await writeConfig(finalConfig);
|
|
6584
7343
|
ui.write(`
|
|
6585
|
-
${
|
|
7344
|
+
${import_picocolors18.default.green("\u2713")} ${import_picocolors18.default.white("Agent configured.")}
|
|
6586
7345
|
`);
|
|
6587
|
-
ui.info(` Re-run ${
|
|
7346
|
+
ui.info(` Re-run ${import_picocolors18.default.dim(import_picocolors18.default.gray("dora init"))} anytime to change it.
|
|
6588
7347
|
`);
|
|
6589
|
-
ui.info(` Next: ${
|
|
7348
|
+
ui.info(` Next: ${import_picocolors18.default.dim(import_picocolors18.default.gray('dora journal add ".."'))}, ${import_picocolors18.default.dim(import_picocolors18.default.gray("dora journal list"))}, or ${import_picocolors18.default.dim(import_picocolors18.default.gray("dora journal update"))}.
|
|
6590
7349
|
`);
|
|
6591
7350
|
process.exit(0);
|
|
6592
7351
|
}
|
|
@@ -6595,7 +7354,7 @@ Project-specific decisions.
|
|
|
6595
7354
|
|
|
6596
7355
|
// src/core/update.ts
|
|
6597
7356
|
import { resolve as resolve28 } from "path";
|
|
6598
|
-
import { homedir as
|
|
7357
|
+
import { homedir as homedir3 } from "os";
|
|
6599
7358
|
function normalizePath(p) {
|
|
6600
7359
|
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
6601
7360
|
}
|
|
@@ -6773,14 +7532,14 @@ async function readMarker() {
|
|
|
6773
7532
|
async function writeMarker(marker) {
|
|
6774
7533
|
try {
|
|
6775
7534
|
const { mkdir, writeFile } = await import("fs/promises");
|
|
6776
|
-
const { dirname:
|
|
6777
|
-
await mkdir(
|
|
7535
|
+
const { dirname: dirname8 } = await import("path");
|
|
7536
|
+
await mkdir(dirname8(MARKER_PATH), { recursive: true });
|
|
6778
7537
|
await writeFile(MARKER_PATH, JSON.stringify(marker, null, 2));
|
|
6779
7538
|
} catch {}
|
|
6780
7539
|
}
|
|
6781
7540
|
var MARKER_PATH;
|
|
6782
7541
|
var init_update2 = __esm(() => {
|
|
6783
|
-
MARKER_PATH = resolve28(
|
|
7542
|
+
MARKER_PATH = resolve28(homedir3(), ".doraval", "install.json");
|
|
6784
7543
|
});
|
|
6785
7544
|
|
|
6786
7545
|
// src/cli/commands/update.ts
|
|
@@ -6789,7 +7548,7 @@ __export(exports_update2, {
|
|
|
6789
7548
|
default: () => update_default2
|
|
6790
7549
|
});
|
|
6791
7550
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
6792
|
-
import { homedir as
|
|
7551
|
+
import { homedir as homedir4 } from "os";
|
|
6793
7552
|
import { fileURLToPath } from "url";
|
|
6794
7553
|
import { realpath, access } from "fs/promises";
|
|
6795
7554
|
async function confirmUpdate() {
|
|
@@ -6835,7 +7594,7 @@ var init_update3 = __esm(() => {
|
|
|
6835
7594
|
entrypoint,
|
|
6836
7595
|
argv: process.argv,
|
|
6837
7596
|
env: process.env,
|
|
6838
|
-
homeDir:
|
|
7597
|
+
homeDir: homedir4(),
|
|
6839
7598
|
realpath: (p) => realpath(p),
|
|
6840
7599
|
exists: async (p) => {
|
|
6841
7600
|
try {
|
|
@@ -6926,16 +7685,16 @@ var exports_providers = {};
|
|
|
6926
7685
|
__export(exports_providers, {
|
|
6927
7686
|
default: () => providers_default
|
|
6928
7687
|
});
|
|
6929
|
-
var
|
|
7688
|
+
var import_picocolors19, providers_default;
|
|
6930
7689
|
var init_providers2 = __esm(() => {
|
|
6931
7690
|
init_dist();
|
|
6932
7691
|
init_out();
|
|
6933
7692
|
init_spec();
|
|
6934
|
-
|
|
7693
|
+
import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
6935
7694
|
providers_default = defineCommand({
|
|
6936
7695
|
meta: {
|
|
6937
7696
|
name: "providers",
|
|
6938
|
-
description: "List supported providers and their packaging details"
|
|
7697
|
+
description: "List supported providers and their packaging details (including keyword discovery)"
|
|
6939
7698
|
},
|
|
6940
7699
|
args: {
|
|
6941
7700
|
json: {
|
|
@@ -6956,14 +7715,136 @@ var init_providers2 = __esm(() => {
|
|
|
6956
7715
|
for (const id of supportedProviders) {
|
|
6957
7716
|
const spec = getProviderSpec(id);
|
|
6958
7717
|
ui.write(`
|
|
6959
|
-
${
|
|
7718
|
+
${import_picocolors19.default.bold(id)} \u2014 ${spec.name}`);
|
|
6960
7719
|
ui.info(` Manifest: ${spec.manifestPath}`);
|
|
6961
7720
|
ui.info(` Marketplace: ${spec.marketplacePath}`);
|
|
6962
7721
|
ui.info(` MCP: ${spec.mcpFilename}`);
|
|
6963
|
-
ui.info(`
|
|
7722
|
+
ui.info(` Keywords in plugin.json: supported \u2014 If users mention any of these keywords, your plugin will get triggered`);
|
|
7723
|
+
ui.info(` Example: doraval validate . --for ${id}:plugin`);
|
|
6964
7724
|
}
|
|
6965
7725
|
ui.write(`
|
|
6966
7726
|
Use --json for machine-readable output.`);
|
|
7727
|
+
ui.write(` Tip: Add a "keywords" array to your plugin manifest for better agent discovery.`);
|
|
7728
|
+
process.exit(0);
|
|
7729
|
+
}
|
|
7730
|
+
});
|
|
7731
|
+
});
|
|
7732
|
+
|
|
7733
|
+
// src/cli/commands/completion.ts
|
|
7734
|
+
var exports_completion = {};
|
|
7735
|
+
__export(exports_completion, {
|
|
7736
|
+
default: () => completion_default
|
|
7737
|
+
});
|
|
7738
|
+
var commands, subCommands, completion_default;
|
|
7739
|
+
var init_completion = __esm(() => {
|
|
7740
|
+
init_dist();
|
|
7741
|
+
commands = [
|
|
7742
|
+
"validate",
|
|
7743
|
+
"init",
|
|
7744
|
+
"bump",
|
|
7745
|
+
"update",
|
|
7746
|
+
"providers",
|
|
7747
|
+
"skill",
|
|
7748
|
+
"journal",
|
|
7749
|
+
"claude",
|
|
7750
|
+
"codex",
|
|
7751
|
+
"cursor",
|
|
7752
|
+
"copilot"
|
|
7753
|
+
];
|
|
7754
|
+
subCommands = {
|
|
7755
|
+
skill: ["validate", "drift", "judge"],
|
|
7756
|
+
journal: ["init", "list", "context", "hook", "update", "add", "sync"],
|
|
7757
|
+
hook: ["enable", "disable", "status"],
|
|
7758
|
+
claude: ["new", "bump"],
|
|
7759
|
+
codex: ["new", "bump"],
|
|
7760
|
+
cursor: ["new", "bump"],
|
|
7761
|
+
copilot: ["new", "bump"]
|
|
7762
|
+
};
|
|
7763
|
+
completion_default = defineCommand({
|
|
7764
|
+
meta: {
|
|
7765
|
+
name: "completion",
|
|
7766
|
+
description: "Generate shell completion scripts (bash, zsh, fish)"
|
|
7767
|
+
},
|
|
7768
|
+
args: {
|
|
7769
|
+
shell: {
|
|
7770
|
+
type: "positional",
|
|
7771
|
+
description: "Shell to generate completion for (bash | zsh | fish)",
|
|
7772
|
+
required: true
|
|
7773
|
+
}
|
|
7774
|
+
},
|
|
7775
|
+
run({ args }) {
|
|
7776
|
+
const shell = String(args.shell).toLowerCase();
|
|
7777
|
+
if (shell === "bash") {
|
|
7778
|
+
console.log(`# doraval bash completion
|
|
7779
|
+
_doraval_completions() {
|
|
7780
|
+
local cur prev
|
|
7781
|
+
COMPREPLY=()
|
|
7782
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
7783
|
+
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
7784
|
+
|
|
7785
|
+
if [ $COMP_CWORD -eq 1 ]; then
|
|
7786
|
+
COMPREPLY=( $(compgen -W "${commands.join(" ")}" -- "$cur") )
|
|
7787
|
+
elif [ $COMP_CWORD -eq 2 ]; then
|
|
7788
|
+
case "$prev" in
|
|
7789
|
+
skill) COMPREPLY=( $(compgen -W "${subCommands.skill.join(" ")}" -- "$cur") ) ;;
|
|
7790
|
+
journal) COMPREPLY=( $(compgen -W "${subCommands.journal.join(" ")}" -- "$cur") ) ;;
|
|
7791
|
+
hook) COMPREPLY=( $(compgen -W "${subCommands.hook.join(" ")}" -- "$cur") ) ;;
|
|
7792
|
+
claude|codex|cursor|copilot) COMPREPLY=( $(compgen -W "${subCommands.claude.join(" ")}" -- "$cur") ) ;;
|
|
7793
|
+
esac
|
|
7794
|
+
fi
|
|
7795
|
+
}
|
|
7796
|
+
complete -F _doraval_completions doraval
|
|
7797
|
+
`);
|
|
7798
|
+
} else if (shell === "zsh") {
|
|
7799
|
+
console.log(`# doraval zsh completion
|
|
7800
|
+
#compdef doraval
|
|
7801
|
+
|
|
7802
|
+
_doraval() {
|
|
7803
|
+
local -a commands sub
|
|
7804
|
+
commands=(validate init bump update providers skill journal claude codex cursor copilot)
|
|
7805
|
+
_arguments -C \\
|
|
7806
|
+
'1: :->cmd' \\
|
|
7807
|
+
'*::arg:->args'
|
|
7808
|
+
|
|
7809
|
+
case $state in
|
|
7810
|
+
cmd)
|
|
7811
|
+
_describe 'command' commands
|
|
7812
|
+
;;
|
|
7813
|
+
args)
|
|
7814
|
+
case $words[1] in
|
|
7815
|
+
skill)
|
|
7816
|
+
_describe 'subcommand' (validate drift judge)
|
|
7817
|
+
;;
|
|
7818
|
+
journal)
|
|
7819
|
+
_describe 'subcommand' (init list context hook update add sync)
|
|
7820
|
+
;;
|
|
7821
|
+
hook)
|
|
7822
|
+
_describe 'subcommand' (enable disable status)
|
|
7823
|
+
;;
|
|
7824
|
+
claude|codex|cursor|copilot)
|
|
7825
|
+
_describe 'subcommand' (new bump)
|
|
7826
|
+
;;
|
|
7827
|
+
esac
|
|
7828
|
+
;;
|
|
7829
|
+
esac
|
|
7830
|
+
}
|
|
7831
|
+
|
|
7832
|
+
_doraval "$@"
|
|
7833
|
+
`);
|
|
7834
|
+
} else if (shell === "fish") {
|
|
7835
|
+
console.log(`# doraval fish completion
|
|
7836
|
+
complete -c doraval -f
|
|
7837
|
+
complete -c doraval -n '__fish_use_subcommand' -a 'validate init bump update providers skill journal claude codex cursor copilot'
|
|
7838
|
+
|
|
7839
|
+
complete -c doraval -n '__fish_seen_subcommand_from skill' -a 'validate drift judge'
|
|
7840
|
+
complete -c doraval -n '__fish_seen_subcommand_from journal' -a 'init list context hook update add sync'
|
|
7841
|
+
complete -c doraval -n '__fish_seen_subcommand_from hook' -a 'enable disable status'
|
|
7842
|
+
complete -c doraval -n '__fish_seen_subcommand_from claude codex cursor copilot' -a 'new bump'
|
|
7843
|
+
`);
|
|
7844
|
+
} else {
|
|
7845
|
+
console.error(`Unsupported shell: ${shell}. Supported: bash, zsh, fish`);
|
|
7846
|
+
process.exit(1);
|
|
7847
|
+
}
|
|
6967
7848
|
process.exit(0);
|
|
6968
7849
|
}
|
|
6969
7850
|
});
|
|
@@ -6972,7 +7853,7 @@ var init_providers2 = __esm(() => {
|
|
|
6972
7853
|
// src/cli/index.ts
|
|
6973
7854
|
init_dist();
|
|
6974
7855
|
var import__package = __toESM(require_package(), 1);
|
|
6975
|
-
var
|
|
7856
|
+
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
6976
7857
|
var skill = defineCommand({
|
|
6977
7858
|
meta: {
|
|
6978
7859
|
name: "skill",
|
|
@@ -6984,6 +7865,9 @@ var skill = defineCommand({
|
|
|
6984
7865
|
judge: () => Promise.resolve().then(() => (init_judge(), exports_judge)).then((m) => m.default)
|
|
6985
7866
|
},
|
|
6986
7867
|
run() {
|
|
7868
|
+
const cliArgs = process.argv.slice(2);
|
|
7869
|
+
if (cliArgs[0] === "skill" && cliArgs.length > 1)
|
|
7870
|
+
return;
|
|
6987
7871
|
showUsage(skill);
|
|
6988
7872
|
}
|
|
6989
7873
|
});
|
|
@@ -6995,11 +7879,16 @@ var journal = defineCommand({
|
|
|
6995
7879
|
subCommands: {
|
|
6996
7880
|
init: () => Promise.resolve().then(() => (init_init(), exports_init)).then((m) => m.default),
|
|
6997
7881
|
list: () => Promise.resolve().then(() => (init_list(), exports_list)).then((m) => m.default),
|
|
7882
|
+
context: () => Promise.resolve().then(() => (init_context(), exports_context)).then((m) => m.default),
|
|
7883
|
+
hook: () => Promise.resolve().then(() => (init_hook(), exports_hook)).then((m) => m.default),
|
|
6998
7884
|
update: () => Promise.resolve().then(() => (init_update(), exports_update)).then((m) => m.default),
|
|
6999
7885
|
add: () => Promise.resolve().then(() => (init_add(), exports_add)).then((m) => m.default),
|
|
7000
7886
|
sync: () => Promise.resolve().then(() => (init_sync(), exports_sync)).then((m) => m.default)
|
|
7001
7887
|
},
|
|
7002
7888
|
run() {
|
|
7889
|
+
const cliArgs = process.argv.slice(2);
|
|
7890
|
+
if (cliArgs[0] === "journal" && cliArgs.length > 1)
|
|
7891
|
+
return;
|
|
7003
7892
|
showUsage(journal);
|
|
7004
7893
|
}
|
|
7005
7894
|
});
|
|
@@ -7013,6 +7902,9 @@ var claude = defineCommand({
|
|
|
7013
7902
|
bump: () => Promise.resolve().then(() => (init_bump(), exports_bump)).then((m) => m.default)
|
|
7014
7903
|
},
|
|
7015
7904
|
run() {
|
|
7905
|
+
const cliArgs = process.argv.slice(2);
|
|
7906
|
+
if (cliArgs[0] === "claude" && cliArgs.length > 1)
|
|
7907
|
+
return;
|
|
7016
7908
|
showUsage(claude);
|
|
7017
7909
|
}
|
|
7018
7910
|
});
|
|
@@ -7026,6 +7918,9 @@ var codex = defineCommand({
|
|
|
7026
7918
|
bump: () => Promise.resolve().then(() => (init_bump(), exports_bump)).then((m) => m.default)
|
|
7027
7919
|
},
|
|
7028
7920
|
run() {
|
|
7921
|
+
const cliArgs = process.argv.slice(2);
|
|
7922
|
+
if (cliArgs[0] === "codex" && cliArgs.length > 1)
|
|
7923
|
+
return;
|
|
7029
7924
|
showUsage(codex);
|
|
7030
7925
|
}
|
|
7031
7926
|
});
|
|
@@ -7039,6 +7934,9 @@ var cursor = defineCommand({
|
|
|
7039
7934
|
bump: () => Promise.resolve().then(() => (init_bump(), exports_bump)).then((m) => m.default)
|
|
7040
7935
|
},
|
|
7041
7936
|
run() {
|
|
7937
|
+
const cliArgs = process.argv.slice(2);
|
|
7938
|
+
if (cliArgs[0] === "cursor" && cliArgs.length > 1)
|
|
7939
|
+
return;
|
|
7042
7940
|
showUsage(cursor);
|
|
7043
7941
|
}
|
|
7044
7942
|
});
|
|
@@ -7052,9 +7950,38 @@ var copilot = defineCommand({
|
|
|
7052
7950
|
bump: () => Promise.resolve().then(() => (init_bump(), exports_bump)).then((m) => m.default)
|
|
7053
7951
|
},
|
|
7054
7952
|
run() {
|
|
7953
|
+
const cliArgs = process.argv.slice(2);
|
|
7954
|
+
if (cliArgs[0] === "copilot" && cliArgs.length > 1)
|
|
7955
|
+
return;
|
|
7055
7956
|
showUsage(copilot);
|
|
7056
7957
|
}
|
|
7057
7958
|
});
|
|
7959
|
+
var ui2 = defineCommand({
|
|
7960
|
+
meta: {
|
|
7961
|
+
name: "ui",
|
|
7962
|
+
description: "Launch the local doraval web dashboard (no more typing commands for common tasks)"
|
|
7963
|
+
},
|
|
7964
|
+
args: {
|
|
7965
|
+
port: {
|
|
7966
|
+
type: "string",
|
|
7967
|
+
description: "Port to run the local UI server on (default 3737)",
|
|
7968
|
+
default: "3737"
|
|
7969
|
+
},
|
|
7970
|
+
open: {
|
|
7971
|
+
type: "boolean",
|
|
7972
|
+
description: "Automatically open the dashboard in your browser",
|
|
7973
|
+
default: true
|
|
7974
|
+
},
|
|
7975
|
+
host: {
|
|
7976
|
+
type: "string",
|
|
7977
|
+
description: "Host to bind (default 127.0.0.1 for local only)",
|
|
7978
|
+
default: "127.0.0.1"
|
|
7979
|
+
}
|
|
7980
|
+
},
|
|
7981
|
+
async run({ args }) {
|
|
7982
|
+
await Promise.resolve().then(() => (init_ui(), exports_ui)).then((m) => m.default.run({ args }));
|
|
7983
|
+
}
|
|
7984
|
+
});
|
|
7058
7985
|
var doraemonArt = `
|
|
7059
7986
|
\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2880\u28E0\u28E4\u28F4\u28F6\u28F6\u28F6\u28F6\u28F6\u2836\u28F6\u28E4\u28E4\u28C0\u2800\u2800\u2800\u2800\u2800\u2800
|
|
7060
7987
|
\u2800\u2800\u2800\u2800\u2800\u2800\u2800\u2880\u28E4\u28FE\u28FF\u28FF\u28FF\u2801\u2800\u2880\u2808\u28BF\u2880\u28C0\u2800\u2839\u28FF\u28FF\u28FF\u28E6\u28C4\u2800\u2800\u2800
|
|
@@ -7079,17 +8006,24 @@ var main = defineCommand({
|
|
|
7079
8006
|
bump: () => Promise.resolve().then(() => (init_bump(), exports_bump)).then((m) => m.default),
|
|
7080
8007
|
update: () => Promise.resolve().then(() => (init_update3(), exports_update2)).then((m) => m.default),
|
|
7081
8008
|
providers: () => Promise.resolve().then(() => (init_providers2(), exports_providers)).then((m) => m.default),
|
|
8009
|
+
completion: () => Promise.resolve().then(() => (init_completion(), exports_completion)).then((m) => m.default),
|
|
7082
8010
|
skill: () => Promise.resolve(skill),
|
|
7083
8011
|
journal: () => Promise.resolve(journal),
|
|
7084
8012
|
claude: () => Promise.resolve(claude),
|
|
7085
8013
|
codex: () => Promise.resolve(codex),
|
|
7086
8014
|
cursor: () => Promise.resolve(cursor),
|
|
7087
|
-
copilot: () => Promise.resolve(copilot)
|
|
8015
|
+
copilot: () => Promise.resolve(copilot),
|
|
8016
|
+
ui: () => Promise.resolve(ui2)
|
|
7088
8017
|
},
|
|
7089
8018
|
run() {
|
|
7090
|
-
|
|
7091
|
-
|
|
8019
|
+
const cliArgs = process.argv.slice(2);
|
|
8020
|
+
if (cliArgs.length > 0)
|
|
8021
|
+
return;
|
|
8022
|
+
if (process.stdout.isTTY) {
|
|
8023
|
+
console.error(`
|
|
8024
|
+
` + import_picocolors20.default.blue(doraemonArt) + `
|
|
7092
8025
|
`);
|
|
8026
|
+
}
|
|
7093
8027
|
showUsage(main);
|
|
7094
8028
|
}
|
|
7095
8029
|
});
|