jioogen 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +558 -69
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -363,9 +363,9 @@ var require_utils = __commonJS({
|
|
|
363
363
|
fpath.split(self.sep).forEach(function(name) {
|
|
364
364
|
if (!name || name.substr(-1, 1) === ":") return;
|
|
365
365
|
resolvedPath += self.sep + name;
|
|
366
|
-
var
|
|
366
|
+
var stat3;
|
|
367
367
|
try {
|
|
368
|
-
|
|
368
|
+
stat3 = self.fs.statSync(resolvedPath);
|
|
369
369
|
} catch (e) {
|
|
370
370
|
if (e.message && e.message.startsWith("ENOENT")) {
|
|
371
371
|
self.fs.mkdirSync(resolvedPath);
|
|
@@ -373,7 +373,7 @@ var require_utils = __commonJS({
|
|
|
373
373
|
throw e;
|
|
374
374
|
}
|
|
375
375
|
}
|
|
376
|
-
if (
|
|
376
|
+
if (stat3 && stat3.isFile()) throw Errors.FILE_IN_THE_WAY(`"${resolvedPath}"`);
|
|
377
377
|
});
|
|
378
378
|
}
|
|
379
379
|
mkdirSync(folder);
|
|
@@ -382,8 +382,8 @@ var require_utils = __commonJS({
|
|
|
382
382
|
const self = this;
|
|
383
383
|
if (self.fs.existsSync(path)) {
|
|
384
384
|
if (!overwrite) return false;
|
|
385
|
-
var
|
|
386
|
-
if (
|
|
385
|
+
var stat3 = self.fs.statSync(path);
|
|
386
|
+
if (stat3.isDirectory()) {
|
|
387
387
|
return false;
|
|
388
388
|
}
|
|
389
389
|
}
|
|
@@ -416,8 +416,8 @@ var require_utils = __commonJS({
|
|
|
416
416
|
const self = this;
|
|
417
417
|
self.fs.exists(path, function(exist) {
|
|
418
418
|
if (exist && !overwrite) return callback(false);
|
|
419
|
-
self.fs.stat(path, function(err,
|
|
420
|
-
if (exist &&
|
|
419
|
+
self.fs.stat(path, function(err, stat3) {
|
|
420
|
+
if (exist && stat3 && stat3.isDirectory()) {
|
|
421
421
|
return callback(false);
|
|
422
422
|
}
|
|
423
423
|
var folder = pth.dirname(path);
|
|
@@ -469,13 +469,13 @@ var require_utils = __commonJS({
|
|
|
469
469
|
for (const part of rel.split(pth.sep)) {
|
|
470
470
|
if (!part || part === ".") continue;
|
|
471
471
|
cur = pth.join(cur, part);
|
|
472
|
-
let
|
|
472
|
+
let stat3;
|
|
473
473
|
try {
|
|
474
|
-
|
|
474
|
+
stat3 = self.fs.lstatSync(cur);
|
|
475
475
|
} catch (e) {
|
|
476
476
|
break;
|
|
477
477
|
}
|
|
478
|
-
if (
|
|
478
|
+
if (stat3.isSymbolicLink()) throw Errors.FILE_IN_THE_WAY(`"${cur}"`);
|
|
479
479
|
}
|
|
480
480
|
};
|
|
481
481
|
Utils.prototype.findFiles = function(path) {
|
|
@@ -502,11 +502,11 @@ var require_utils = __commonJS({
|
|
|
502
502
|
self.fs.readdirSync(dir).forEach(function(file) {
|
|
503
503
|
const path2 = pth.join(dir, file);
|
|
504
504
|
if (escapesRoot(path2)) return;
|
|
505
|
-
const
|
|
505
|
+
const stat3 = self.fs.statSync(path2);
|
|
506
506
|
if (!pattern || pattern.test(path2)) {
|
|
507
|
-
files.push(pth.normalize(path2) + (
|
|
507
|
+
files.push(pth.normalize(path2) + (stat3.isDirectory() ? self.sep : ""));
|
|
508
508
|
}
|
|
509
|
-
if (
|
|
509
|
+
if (stat3.isDirectory() && recursive) {
|
|
510
510
|
const realDir = self.fs.realpathSync(path2);
|
|
511
511
|
if (!visited.has(realDir)) {
|
|
512
512
|
visited.add(realDir);
|
|
@@ -553,14 +553,14 @@ var require_utils = __commonJS({
|
|
|
553
553
|
if (!--pending) done();
|
|
554
554
|
return;
|
|
555
555
|
}
|
|
556
|
-
self.fs.stat(file, function(err3,
|
|
556
|
+
self.fs.stat(file, function(err3, stat3) {
|
|
557
557
|
if (err3) return done(err3);
|
|
558
|
-
if (!
|
|
558
|
+
if (!stat3) {
|
|
559
559
|
if (!--pending) done();
|
|
560
560
|
return;
|
|
561
561
|
}
|
|
562
|
-
results.push(pth.normalize(file) + (
|
|
563
|
-
if (!
|
|
562
|
+
results.push(pth.normalize(file) + (stat3.isDirectory() ? self.sep : ""));
|
|
563
|
+
if (!stat3.isDirectory()) {
|
|
564
564
|
if (!--pending) done();
|
|
565
565
|
return;
|
|
566
566
|
}
|
|
@@ -2978,7 +2978,7 @@ var ApiClient = class {
|
|
|
2978
2978
|
};
|
|
2979
2979
|
|
|
2980
2980
|
// src/commands.ts
|
|
2981
|
-
import { readdir as
|
|
2981
|
+
import { readdir as readdir4, readFile as readFile6 } from "node:fs/promises";
|
|
2982
2982
|
import { join as join6, resolve as resolve4 } from "node:path";
|
|
2983
2983
|
|
|
2984
2984
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
@@ -8766,15 +8766,28 @@ function delay(ms) {
|
|
|
8766
8766
|
}
|
|
8767
8767
|
|
|
8768
8768
|
// src/skills.ts
|
|
8769
|
-
import {
|
|
8769
|
+
import { createHash } from "node:crypto";
|
|
8770
|
+
import { cp, mkdir as mkdir3, readdir as readdir2, readFile as readFile4, rm as rm3, stat as stat2, writeFile as writeFile3 } from "node:fs/promises";
|
|
8771
|
+
import { homedir as homedir2 } from "node:os";
|
|
8770
8772
|
import { dirname as dirname3, join as join4, resolve as resolve2 } from "node:path";
|
|
8773
|
+
import { fileURLToPath } from "node:url";
|
|
8771
8774
|
var EXECUTION_SKILL_NAME = "jioogen-execution";
|
|
8772
8775
|
var AGENT_SKILL_DIRS = [
|
|
8773
8776
|
".claude/skills",
|
|
8777
|
+
".opencode/skills",
|
|
8774
8778
|
".agent/skills",
|
|
8775
8779
|
".codex/skills",
|
|
8776
8780
|
".cursor/skills"
|
|
8777
8781
|
];
|
|
8782
|
+
var SKILL_PACK_NAMES = ["baseline", "ui-quality", "backend-rules"];
|
|
8783
|
+
var SKILL_PACKS = {
|
|
8784
|
+
baseline: ["karpathy-guidelines"],
|
|
8785
|
+
"ui-quality": ["ui-ux-pro-max", "impeccable"],
|
|
8786
|
+
"backend-rules": []
|
|
8787
|
+
};
|
|
8788
|
+
var HARNESS_MANIFEST_PATH = ".jioogen/harness.json";
|
|
8789
|
+
var HARNESS_SCHEMA_VERSION = 1;
|
|
8790
|
+
var FRONTEND_MOCK_LAYER_RE = /^[ \t]*(?:[-*][ \t]+)?layer[ \t]*[:=][ \t]*[`"']?frontend_mock\b/im;
|
|
8778
8791
|
var MARKER_PREFIX = "<!-- jioogen-execution-skill:";
|
|
8779
8792
|
var MARKER = `${MARKER_PREFIX} v1 -->`;
|
|
8780
8793
|
var JIOOGEN_AGENTS_MD = `${MARKER}
|
|
@@ -8829,7 +8842,7 @@ mengetik "lanjut".
|
|
|
8829
8842
|
|
|
8830
8843
|
## 6. Skill eksekusi
|
|
8831
8844
|
Baca skill \`${EXECUTION_SKILL_NAME}\` di awal prompt
|
|
8832
|
-
(\`.claude/skills\`, \`.agent/skills\`, \`.codex/skills\`, atau \`.cursor/skills\`).
|
|
8845
|
+
(\`.claude/skills\`, \`.opencode/skills\`, \`.agent/skills\`, \`.codex/skills\`, atau \`.cursor/skills\`).
|
|
8833
8846
|
Skill itu memuat kontrak lengkap; file ini adalah ringkasan yang mengikat.
|
|
8834
8847
|
`;
|
|
8835
8848
|
var JIOOGEN_EXECUTION_SKILL = `---
|
|
@@ -8929,10 +8942,247 @@ async function readFileSafe(path) {
|
|
|
8929
8942
|
return void 0;
|
|
8930
8943
|
}
|
|
8931
8944
|
}
|
|
8932
|
-
|
|
8945
|
+
function isRecord2(value) {
|
|
8946
|
+
return typeof value === "object" && value !== null;
|
|
8947
|
+
}
|
|
8948
|
+
function isMissingFile3(error) {
|
|
8949
|
+
return isRecord2(error) && error.code === "ENOENT";
|
|
8950
|
+
}
|
|
8951
|
+
async function isFile(path) {
|
|
8952
|
+
try {
|
|
8953
|
+
return (await stat2(path)).isFile();
|
|
8954
|
+
} catch (error) {
|
|
8955
|
+
if (isMissingFile3(error)) return false;
|
|
8956
|
+
throw error;
|
|
8957
|
+
}
|
|
8958
|
+
}
|
|
8959
|
+
async function isDirectory(path) {
|
|
8960
|
+
try {
|
|
8961
|
+
return (await stat2(path)).isDirectory();
|
|
8962
|
+
} catch (error) {
|
|
8963
|
+
if (isMissingFile3(error)) return false;
|
|
8964
|
+
throw error;
|
|
8965
|
+
}
|
|
8966
|
+
}
|
|
8967
|
+
function isSkillPackName(value) {
|
|
8968
|
+
return typeof value === "string" && SKILL_PACK_NAMES.includes(value);
|
|
8969
|
+
}
|
|
8970
|
+
function isProductKind(value) {
|
|
8971
|
+
return value === "ui" || value === "non-ui";
|
|
8972
|
+
}
|
|
8973
|
+
function parseSkillEntry(value) {
|
|
8974
|
+
if (!isRecord2(value)) return null;
|
|
8975
|
+
const { name, source, sha256 } = value;
|
|
8976
|
+
if (typeof name !== "string" || name.length === 0) return null;
|
|
8977
|
+
if (typeof source !== "string" || source.length === 0) return null;
|
|
8978
|
+
if (typeof sha256 !== "string" || sha256.length === 0) return null;
|
|
8979
|
+
return { name, source, sha256 };
|
|
8980
|
+
}
|
|
8981
|
+
function parsePack(value) {
|
|
8982
|
+
if (!isRecord2(value)) return null;
|
|
8983
|
+
const { name, skills, missing } = value;
|
|
8984
|
+
if (!isSkillPackName(name)) return null;
|
|
8985
|
+
if (!Array.isArray(skills) || !Array.isArray(missing)) return null;
|
|
8986
|
+
const parsedSkills = [];
|
|
8987
|
+
for (const entry of skills) {
|
|
8988
|
+
const parsed = parseSkillEntry(entry);
|
|
8989
|
+
if (parsed === null) return null;
|
|
8990
|
+
parsedSkills.push(parsed);
|
|
8991
|
+
}
|
|
8992
|
+
const parsedMissing = [];
|
|
8993
|
+
for (const entry of missing) {
|
|
8994
|
+
if (typeof entry !== "string") return null;
|
|
8995
|
+
parsedMissing.push(entry);
|
|
8996
|
+
}
|
|
8997
|
+
return { name, skills: parsedSkills, missing: parsedMissing };
|
|
8998
|
+
}
|
|
8999
|
+
function parseHarnessManifest(value) {
|
|
9000
|
+
if (!isRecord2(value)) return null;
|
|
9001
|
+
const { schemaVersion, productKind, packs, updatedAt } = value;
|
|
9002
|
+
if (schemaVersion !== HARNESS_SCHEMA_VERSION) return null;
|
|
9003
|
+
if (!isProductKind(productKind)) return null;
|
|
9004
|
+
if (!Array.isArray(packs)) return null;
|
|
9005
|
+
const parsedPacks = [];
|
|
9006
|
+
for (const entry of packs) {
|
|
9007
|
+
const parsed = parsePack(entry);
|
|
9008
|
+
if (parsed === null) return null;
|
|
9009
|
+
parsedPacks.push(parsed);
|
|
9010
|
+
}
|
|
9011
|
+
if (typeof updatedAt !== "string" || updatedAt.length === 0) return null;
|
|
9012
|
+
return { schemaVersion: HARNESS_SCHEMA_VERSION, productKind, packs: parsedPacks, updatedAt };
|
|
9013
|
+
}
|
|
9014
|
+
async function readHarnessManifest(bundleDir) {
|
|
9015
|
+
const path = join4(resolve2(bundleDir), HARNESS_MANIFEST_PATH);
|
|
9016
|
+
let raw;
|
|
9017
|
+
try {
|
|
9018
|
+
raw = await readFile4(path, "utf8");
|
|
9019
|
+
} catch (error) {
|
|
9020
|
+
if (isMissingFile3(error)) return null;
|
|
9021
|
+
throw error;
|
|
9022
|
+
}
|
|
9023
|
+
try {
|
|
9024
|
+
return parseHarnessManifest(JSON.parse(raw));
|
|
9025
|
+
} catch {
|
|
9026
|
+
return null;
|
|
9027
|
+
}
|
|
9028
|
+
}
|
|
9029
|
+
async function writeHarnessManifest(bundleDir, manifest) {
|
|
9030
|
+
const path = join4(resolve2(bundleDir), HARNESS_MANIFEST_PATH);
|
|
9031
|
+
await mkdir3(dirname3(path), { recursive: true });
|
|
9032
|
+
await writeFile3(path, `${JSON.stringify(manifest, null, 2)}
|
|
9033
|
+
`, "utf8");
|
|
9034
|
+
return path;
|
|
9035
|
+
}
|
|
9036
|
+
async function detectProductKind(bundleDir) {
|
|
9037
|
+
const tasksDir = join4(resolve2(bundleDir), "tasks");
|
|
9038
|
+
let entries;
|
|
9039
|
+
try {
|
|
9040
|
+
entries = await readdir2(tasksDir, { withFileTypes: true });
|
|
9041
|
+
} catch (error) {
|
|
9042
|
+
if (isMissingFile3(error)) return "non-ui";
|
|
9043
|
+
throw error;
|
|
9044
|
+
}
|
|
9045
|
+
for (const entry of entries) {
|
|
9046
|
+
if (!entry.isFile() || !entry.name.toLowerCase().endsWith(".md")) continue;
|
|
9047
|
+
const content = await readFileSafe(join4(tasksDir, entry.name));
|
|
9048
|
+
if (content !== void 0 && FRONTEND_MOCK_LAYER_RE.test(content)) return "ui";
|
|
9049
|
+
}
|
|
9050
|
+
return "non-ui";
|
|
9051
|
+
}
|
|
9052
|
+
function packsForProduct(productKind) {
|
|
9053
|
+
return productKind === "ui" ? ["baseline", "ui-quality", "backend-rules"] : ["baseline", "backend-rules"];
|
|
9054
|
+
}
|
|
9055
|
+
function cliPackageRoot() {
|
|
9056
|
+
return resolve2(dirname3(fileURLToPath(import.meta.url)), "..");
|
|
9057
|
+
}
|
|
9058
|
+
function skillSourceCandidates(pack, name) {
|
|
9059
|
+
const candidates = [];
|
|
9060
|
+
const override = process.env.JIOOGEN_SKILLS_DIR?.trim();
|
|
9061
|
+
if (override !== void 0 && override.length > 0) candidates.push(join4(override, name));
|
|
9062
|
+
candidates.push(join4(configDir(), "skill-cache", name));
|
|
9063
|
+
candidates.push(join4(cliPackageRoot(), "skills-pack", pack, name));
|
|
9064
|
+
const home = homedir2();
|
|
9065
|
+
candidates.push(join4(home, ".agents", "skills", name));
|
|
9066
|
+
candidates.push(join4(home, ".config", "opencode", "skills", name));
|
|
9067
|
+
candidates.push(join4(home, ".claude", "skills", name));
|
|
9068
|
+
return candidates;
|
|
9069
|
+
}
|
|
9070
|
+
async function resolveSkillSource(pack, name) {
|
|
9071
|
+
for (const candidate of skillSourceCandidates(pack, name)) {
|
|
9072
|
+
if (await isDirectory(candidate) && await isFile(join4(candidate, "SKILL.md"))) {
|
|
9073
|
+
return candidate;
|
|
9074
|
+
}
|
|
9075
|
+
}
|
|
9076
|
+
return null;
|
|
9077
|
+
}
|
|
9078
|
+
async function collectRelativeFiles(root, prefix) {
|
|
9079
|
+
const entries = await readdir2(join4(root, prefix), { withFileTypes: true });
|
|
9080
|
+
const files = [];
|
|
9081
|
+
for (const entry of entries) {
|
|
9082
|
+
const relative2 = prefix.length === 0 ? entry.name : `${prefix}/${entry.name}`;
|
|
9083
|
+
if (entry.isDirectory()) {
|
|
9084
|
+
files.push(...await collectRelativeFiles(root, relative2));
|
|
9085
|
+
} else if (entry.isFile()) {
|
|
9086
|
+
files.push(relative2);
|
|
9087
|
+
}
|
|
9088
|
+
}
|
|
9089
|
+
return files;
|
|
9090
|
+
}
|
|
9091
|
+
async function hashSkillDirectory(directory) {
|
|
9092
|
+
const root = resolve2(directory);
|
|
9093
|
+
const files = (await collectRelativeFiles(root, "")).sort();
|
|
9094
|
+
const hash = createHash("sha256");
|
|
9095
|
+
for (const relative2 of files) {
|
|
9096
|
+
hash.update(relative2);
|
|
9097
|
+
hash.update("\0");
|
|
9098
|
+
hash.update(await readFile4(join4(root, ...relative2.split("/"))));
|
|
9099
|
+
hash.update("\0");
|
|
9100
|
+
}
|
|
9101
|
+
return hash.digest("hex");
|
|
9102
|
+
}
|
|
9103
|
+
async function replaceDirectory(source, target) {
|
|
9104
|
+
await rm3(target, { recursive: true, force: true });
|
|
9105
|
+
await mkdir3(dirname3(target), { recursive: true });
|
|
9106
|
+
await cp(source, target, { recursive: true });
|
|
9107
|
+
}
|
|
9108
|
+
function packNamesInCanonicalOrder(names) {
|
|
9109
|
+
const wanted = new Set(names);
|
|
9110
|
+
return SKILL_PACK_NAMES.filter((name) => wanted.has(name));
|
|
9111
|
+
}
|
|
9112
|
+
async function provisionPack(root, pack, prior, written, preserved) {
|
|
9113
|
+
const skills = [];
|
|
9114
|
+
const missing = [];
|
|
9115
|
+
for (const name of SKILL_PACKS[pack]) {
|
|
9116
|
+
const source = await resolveSkillSource(pack, name);
|
|
9117
|
+
if (source === null) {
|
|
9118
|
+
missing.push(name);
|
|
9119
|
+
continue;
|
|
9120
|
+
}
|
|
9121
|
+
const sha256 = await hashSkillDirectory(source);
|
|
9122
|
+
const previous = prior?.skills.find((entry) => entry.name === name);
|
|
9123
|
+
let owned = previous !== void 0;
|
|
9124
|
+
for (const dir of AGENT_SKILL_DIRS) {
|
|
9125
|
+
const targetDir = join4(root, dir, name);
|
|
9126
|
+
const skillMd = join4(targetDir, "SKILL.md");
|
|
9127
|
+
if (await isFile(skillMd)) {
|
|
9128
|
+
const targetHash = await hashSkillDirectory(targetDir);
|
|
9129
|
+
if (previous === void 0) {
|
|
9130
|
+
preserved.push(`${dir}/${name}/SKILL.md`);
|
|
9131
|
+
continue;
|
|
9132
|
+
}
|
|
9133
|
+
if (targetHash === sha256) {
|
|
9134
|
+
owned = true;
|
|
9135
|
+
continue;
|
|
9136
|
+
}
|
|
9137
|
+
await replaceDirectory(source, targetDir);
|
|
9138
|
+
written.push(`${dir}/${name}/SKILL.md`);
|
|
9139
|
+
owned = true;
|
|
9140
|
+
continue;
|
|
9141
|
+
}
|
|
9142
|
+
await replaceDirectory(source, targetDir);
|
|
9143
|
+
written.push(`${dir}/${name}/SKILL.md`);
|
|
9144
|
+
owned = true;
|
|
9145
|
+
}
|
|
9146
|
+
if (owned) skills.push({ name, source, sha256 });
|
|
9147
|
+
}
|
|
9148
|
+
return { name: pack, skills, missing };
|
|
9149
|
+
}
|
|
9150
|
+
function sameManifestBody(manifest, productKind, packs) {
|
|
9151
|
+
return manifest.productKind === productKind && JSON.stringify(manifest.packs) === JSON.stringify(packs);
|
|
9152
|
+
}
|
|
9153
|
+
async function provisionSkillPacks(bundleDir, packNames, productKind) {
|
|
9154
|
+
const root = resolve2(bundleDir);
|
|
9155
|
+
const existing = await readHarnessManifest(root);
|
|
9156
|
+
const written = [];
|
|
9157
|
+
const preserved = [];
|
|
9158
|
+
const packs = [];
|
|
9159
|
+
for (const pack of packNamesInCanonicalOrder(packNames)) {
|
|
9160
|
+
const prior = existing?.packs.find((entry) => entry.name === pack);
|
|
9161
|
+
packs.push(await provisionPack(root, pack, prior, written, preserved));
|
|
9162
|
+
}
|
|
9163
|
+
const changed = existing === null || !sameManifestBody(existing, productKind, packs);
|
|
9164
|
+
const manifest = {
|
|
9165
|
+
schemaVersion: HARNESS_SCHEMA_VERSION,
|
|
9166
|
+
productKind,
|
|
9167
|
+
packs,
|
|
9168
|
+
updatedAt: changed ? (/* @__PURE__ */ new Date()).toISOString() : existing?.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
9169
|
+
};
|
|
9170
|
+
const harnessPath = await writeHarnessManifest(root, manifest);
|
|
9171
|
+
return {
|
|
9172
|
+
written,
|
|
9173
|
+
preserved,
|
|
9174
|
+
productKind,
|
|
9175
|
+
packs,
|
|
9176
|
+
missing: packs.flatMap((pack) => pack.missing),
|
|
9177
|
+
harnessPath
|
|
9178
|
+
};
|
|
9179
|
+
}
|
|
9180
|
+
async function installAgentSkills(bundleDir, options = {}) {
|
|
8933
9181
|
const root = resolve2(bundleDir);
|
|
8934
9182
|
const written = [];
|
|
8935
9183
|
const preserved = [];
|
|
9184
|
+
const productKind = options.productKind ?? await detectProductKind(root);
|
|
9185
|
+
const harnessPath = join4(root, HARNESS_MANIFEST_PATH);
|
|
8936
9186
|
const agentsPath = join4(root, "AGENTS.md");
|
|
8937
9187
|
const existing = await readFileSafe(agentsPath);
|
|
8938
9188
|
if (existing === void 0 || existing.includes(MARKER_PREFIX)) {
|
|
@@ -8948,11 +9198,88 @@ async function installAgentSkills(bundleDir) {
|
|
|
8948
9198
|
await writeFile3(target, JIOOGEN_EXECUTION_SKILL, "utf8");
|
|
8949
9199
|
written.push(relative2);
|
|
8950
9200
|
}
|
|
8951
|
-
|
|
9201
|
+
if (options.noSkills === true) {
|
|
9202
|
+
return { written, preserved, productKind, packs: [], missing: [], harnessPath };
|
|
9203
|
+
}
|
|
9204
|
+
const current = await readHarnessManifest(root);
|
|
9205
|
+
const requested = options.packs ?? packNamesInCanonicalOrder([
|
|
9206
|
+
...packsForProduct(productKind),
|
|
9207
|
+
...current?.packs.map((pack) => pack.name) ?? []
|
|
9208
|
+
]);
|
|
9209
|
+
const provision = await provisionSkillPacks(root, requested, productKind);
|
|
9210
|
+
return {
|
|
9211
|
+
written: [...written, ...provision.written],
|
|
9212
|
+
preserved: [...preserved, ...provision.preserved],
|
|
9213
|
+
productKind,
|
|
9214
|
+
packs: provision.packs,
|
|
9215
|
+
missing: provision.missing,
|
|
9216
|
+
harnessPath: provision.harnessPath
|
|
9217
|
+
};
|
|
9218
|
+
}
|
|
9219
|
+
function parseSkillPackArgument(value) {
|
|
9220
|
+
if (value === "all") return [...SKILL_PACK_NAMES];
|
|
9221
|
+
return isSkillPackName(value) ? [value] : null;
|
|
9222
|
+
}
|
|
9223
|
+
async function addSkillPacks(bundleDir, packNames, options = {}) {
|
|
9224
|
+
const root = resolve2(bundleDir);
|
|
9225
|
+
const existing = await readHarnessManifest(root);
|
|
9226
|
+
const productKind = options.productKind ?? existing?.productKind ?? await detectProductKind(root);
|
|
9227
|
+
const requested = packNamesInCanonicalOrder([
|
|
9228
|
+
...existing?.packs.map((pack) => pack.name) ?? [],
|
|
9229
|
+
...packNames
|
|
9230
|
+
]);
|
|
9231
|
+
const provision = await provisionSkillPacks(root, requested, productKind);
|
|
9232
|
+
return { ...provision, added: packNamesInCanonicalOrder(packNames) };
|
|
9233
|
+
}
|
|
9234
|
+
async function updateSkillPacks(bundleDir) {
|
|
9235
|
+
const root = resolve2(bundleDir);
|
|
9236
|
+
const existing = await readHarnessManifest(root);
|
|
9237
|
+
if (existing === null) return null;
|
|
9238
|
+
return provisionSkillPacks(
|
|
9239
|
+
root,
|
|
9240
|
+
existing.packs.map((pack) => pack.name),
|
|
9241
|
+
existing.productKind
|
|
9242
|
+
);
|
|
9243
|
+
}
|
|
9244
|
+
async function removeSkillPack(bundleDir, pack) {
|
|
9245
|
+
const root = resolve2(bundleDir);
|
|
9246
|
+
const existing = await readHarnessManifest(root);
|
|
9247
|
+
const removed = [];
|
|
9248
|
+
const preserved = [];
|
|
9249
|
+
if (existing === null) return { removed, preserved, packs: [] };
|
|
9250
|
+
const entry = existing.packs.find((candidate) => candidate.name === pack);
|
|
9251
|
+
if (entry !== void 0) {
|
|
9252
|
+
for (const skill of entry.skills) {
|
|
9253
|
+
for (const dir of AGENT_SKILL_DIRS) {
|
|
9254
|
+
const targetDir = join4(root, dir, skill.name);
|
|
9255
|
+
const skillMd = join4(targetDir, "SKILL.md");
|
|
9256
|
+
if (!await isFile(skillMd)) continue;
|
|
9257
|
+
const targetHash = await hashSkillDirectory(targetDir);
|
|
9258
|
+
if (targetHash !== skill.sha256) {
|
|
9259
|
+
preserved.push(`${dir}/${skill.name}/SKILL.md`);
|
|
9260
|
+
continue;
|
|
9261
|
+
}
|
|
9262
|
+
await rm3(targetDir, { recursive: true, force: true });
|
|
9263
|
+
removed.push(`${dir}/${skill.name}/SKILL.md`);
|
|
9264
|
+
}
|
|
9265
|
+
}
|
|
9266
|
+
}
|
|
9267
|
+
const packs = existing.packs.filter((candidate) => candidate.name !== pack);
|
|
9268
|
+
await writeHarnessManifest(root, {
|
|
9269
|
+
...existing,
|
|
9270
|
+
packs,
|
|
9271
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9272
|
+
});
|
|
9273
|
+
return { removed, preserved, packs };
|
|
9274
|
+
}
|
|
9275
|
+
function skillsDisabled(options) {
|
|
9276
|
+
if (options.noSkills === true) return true;
|
|
9277
|
+
const value = process.env.JIOOGEN_NO_SKILLS?.trim().toLowerCase();
|
|
9278
|
+
return value === "1" || value === "true" || value === "yes";
|
|
8952
9279
|
}
|
|
8953
9280
|
|
|
8954
9281
|
// src/tasks.ts
|
|
8955
|
-
import { readdir as
|
|
9282
|
+
import { readdir as readdir3, readFile as readFile5 } from "node:fs/promises";
|
|
8956
9283
|
import { join as join5 } from "node:path";
|
|
8957
9284
|
var TASK_ID_RE2 = /\b(?:TASK|T)-\d{3,}\b/i;
|
|
8958
9285
|
var HEADING_RE = /^#{1,4}\s+(.+)$/m;
|
|
@@ -8964,13 +9291,51 @@ var LAYER_RANK = {
|
|
|
8964
9291
|
verification: 4
|
|
8965
9292
|
};
|
|
8966
9293
|
var IN_PROGRESS = ["in_progress", "verifying"];
|
|
9294
|
+
var QUALITY_HEADING = "## KUALITAS WAJIB (ringkas; detail di skill bila tersedia)";
|
|
9295
|
+
var MAX_CHECKLIST_LINES = 12;
|
|
9296
|
+
var UI_SKILLS = ["ui-ux-pro-max", "impeccable"];
|
|
9297
|
+
var LAYER_CHECKLISTS = {
|
|
9298
|
+
frontend_mock: [
|
|
9299
|
+
"- Reuse token desain yang ada; jangan hardcode warna/spacing bila token tersedia.",
|
|
9300
|
+
"- Tangani semua state UI: loading, empty, error, success.",
|
|
9301
|
+
"- Responsive untuk mobile dan desktop.",
|
|
9302
|
+
"- Aksesibilitas dasar: label, aria, focus, keyboard.",
|
|
9303
|
+
"- Microcopy jelas dan konsisten dengan konteks."
|
|
9304
|
+
],
|
|
9305
|
+
database: [
|
|
9306
|
+
"- Migrasi eksplisit dan aman (reversible, tanpa mengubah migrasi lama).",
|
|
9307
|
+
"- Constraint dan index sesuai pola query.",
|
|
9308
|
+
"- Relasi dan isolation sesuai specs (tenant/owner).",
|
|
9309
|
+
"- Seed idempotent (bisa dijalankan ulang tanpa duplikasi).",
|
|
9310
|
+
"- Tanpa query lintas pemilik."
|
|
9311
|
+
],
|
|
9312
|
+
backend_api: [
|
|
9313
|
+
"- Validasi input di boundary.",
|
|
9314
|
+
"- Error contract konsisten (status + bentuk response).",
|
|
9315
|
+
"- Auth/permission dicek sebelum mutasi.",
|
|
9316
|
+
"- Idempotensi untuk operasi tulis/retry.",
|
|
9317
|
+
"- Logging tanpa secret/PII."
|
|
9318
|
+
],
|
|
9319
|
+
integration: [
|
|
9320
|
+
"- Mock dihapus dari jalur utama.",
|
|
9321
|
+
"- Kontrak CONTRACT-XXX sama dengan implementasi.",
|
|
9322
|
+
"- E2E memakai data nyata.",
|
|
9323
|
+
"- Error path diuji."
|
|
9324
|
+
],
|
|
9325
|
+
verification: [
|
|
9326
|
+
"- Jalankan semua verificationCommand sampai exit 0.",
|
|
9327
|
+
"- Smoke route/navigasi utama.",
|
|
9328
|
+
"- UI states dan responsive dicek.",
|
|
9329
|
+
"- Bukti Acceptance Criteria \xA711."
|
|
9330
|
+
]
|
|
9331
|
+
};
|
|
8967
9332
|
async function loadTasks(bundleDir) {
|
|
8968
9333
|
const tasksDir = join5(bundleDir, "tasks");
|
|
8969
9334
|
let entries;
|
|
8970
9335
|
try {
|
|
8971
|
-
entries = await
|
|
9336
|
+
entries = await readdir3(tasksDir, { withFileTypes: true });
|
|
8972
9337
|
} catch (error) {
|
|
8973
|
-
if (
|
|
9338
|
+
if (isMissingFile4(error)) throw new Error(`Direktori tasks/ tidak ditemukan di ${bundleDir}`);
|
|
8974
9339
|
throw error;
|
|
8975
9340
|
}
|
|
8976
9341
|
const names = entries.filter(
|
|
@@ -8988,7 +9353,7 @@ async function loadTasks(bundleDir) {
|
|
|
8988
9353
|
try {
|
|
8989
9354
|
content = await readFile5(singlePath, "utf8");
|
|
8990
9355
|
} catch (error) {
|
|
8991
|
-
if (
|
|
9356
|
+
if (isMissingFile4(error)) throw new Error(`Tidak ada berkas task di ${tasksDir}`);
|
|
8992
9357
|
throw error;
|
|
8993
9358
|
}
|
|
8994
9359
|
tasks.push(...parseSingleFile(content));
|
|
@@ -9041,7 +9406,7 @@ function buildTask(id, heading, file, content) {
|
|
|
9041
9406
|
dependsOn: context.dependsOn
|
|
9042
9407
|
};
|
|
9043
9408
|
}
|
|
9044
|
-
function renderActiveTask(task) {
|
|
9409
|
+
function renderActiveTask(task, context) {
|
|
9045
9410
|
const lines = [
|
|
9046
9411
|
"## TASK AKTIF \u2014 PRIORITASKAN INI (satu task per giliran)",
|
|
9047
9412
|
`ref: ${task.id}`,
|
|
@@ -9058,14 +9423,23 @@ function renderActiveTask(task) {
|
|
|
9058
9423
|
"- Jangan mengarang fitur/endpoint/kolom di luar PRD.md/specs/.",
|
|
9059
9424
|
"- Detail implementasi (pola, struktur file dalam scope, library yang sudah ada) milikmu; ikuti konvensi proyek.",
|
|
9060
9425
|
"- Jangan mengubah PRD.md/specs/kontrak. Butuh perubahan kontrak? STOP dan laporkan.",
|
|
9061
|
-
"- Selesai: `npx jioogen task complete <ref>`, lalu `npx jioogen sync`."
|
|
9062
|
-
"",
|
|
9063
|
-
"--- ISI TASK (otoritas scope) ---",
|
|
9064
|
-
task.content,
|
|
9065
|
-
"--- AKHIR ISI TASK ---"
|
|
9426
|
+
"- Selesai: `npx jioogen task complete <ref>`, lalu `npx jioogen sync`."
|
|
9066
9427
|
);
|
|
9428
|
+
const checklist = renderQualityChecklist(task.layer, context);
|
|
9429
|
+
if (checklist.length > 0) lines.push("", ...checklist);
|
|
9430
|
+
lines.push("", "--- ISI TASK (otoritas scope) ---", task.content, "--- AKHIR ISI TASK ---");
|
|
9067
9431
|
return lines.join("\n");
|
|
9068
9432
|
}
|
|
9433
|
+
function renderQualityChecklist(layer, context) {
|
|
9434
|
+
if (context === void 0 || layer === void 0) return [];
|
|
9435
|
+
const items = [...LAYER_CHECKLISTS[layer] ?? []];
|
|
9436
|
+
if (layer === "frontend_mock") {
|
|
9437
|
+
const references = UI_SKILLS.filter((skill) => context.skills?.includes(skill) === true);
|
|
9438
|
+
if (references.length > 0) items.push(`- Referensi skill: ${references.join(", ")}`);
|
|
9439
|
+
}
|
|
9440
|
+
if (items.length === 0) return [];
|
|
9441
|
+
return [QUALITY_HEADING, ...items.slice(0, MAX_CHECKLIST_LINES)];
|
|
9442
|
+
}
|
|
9069
9443
|
function resolveLayer(value) {
|
|
9070
9444
|
if (value === void 0) return void 0;
|
|
9071
9445
|
const token = value.trim().replace(/[`*]/g, "").split(/[\s,(]/)[0];
|
|
@@ -9149,7 +9523,7 @@ function isBoundary(previous, next) {
|
|
|
9149
9523
|
}
|
|
9150
9524
|
return previous.phase !== void 0 && next.phase !== previous.phase;
|
|
9151
9525
|
}
|
|
9152
|
-
function
|
|
9526
|
+
function isMissingFile4(error) {
|
|
9153
9527
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
9154
9528
|
}
|
|
9155
9529
|
|
|
@@ -9275,7 +9649,7 @@ async function pullCommand(projectId, options) {
|
|
|
9275
9649
|
await assertPullTarget(target, projectId, options.force === true);
|
|
9276
9650
|
const bytes = await client.getBytes(projectPath(projectId, "/bundle"));
|
|
9277
9651
|
const written = await extractBundle(bytes, target);
|
|
9278
|
-
const skills = await installAgentSkills(target);
|
|
9652
|
+
const skills = await installAgentSkills(target, { noSkills: skillsDisabled(options) });
|
|
9279
9653
|
await writeProjectLink(target, { projectId, apiUrl });
|
|
9280
9654
|
if (options.json === true) {
|
|
9281
9655
|
console.log(JSON.stringify({ projectId, target, files: written, skills }, null, 2));
|
|
@@ -9283,9 +9657,8 @@ async function pullCommand(projectId, options) {
|
|
|
9283
9657
|
}
|
|
9284
9658
|
console.log(`Pulled ${written.length} file ke ${target}`);
|
|
9285
9659
|
console.log("Project ter-bind lewat .jioogen/project.json.");
|
|
9286
|
-
console.log(
|
|
9287
|
-
|
|
9288
|
-
);
|
|
9660
|
+
console.log(`Skills eksekusi terpasang: ${skills.written.length} file.`);
|
|
9661
|
+
printSkillPacks(skills.packs, skills.missing);
|
|
9289
9662
|
}
|
|
9290
9663
|
async function linkCommand(projectId, options) {
|
|
9291
9664
|
const { apiUrl, client } = await resolveApi(options);
|
|
@@ -9293,26 +9666,20 @@ async function linkCommand(projectId, options) {
|
|
|
9293
9666
|
await assertLinkTarget(target, projectId);
|
|
9294
9667
|
await verifyProjectAccess(client, projectId);
|
|
9295
9668
|
await writeProjectLink(target, { projectId, apiUrl });
|
|
9296
|
-
const skills = await installAgentSkills(target);
|
|
9669
|
+
const skills = await installAgentSkills(target, { noSkills: skillsDisabled(options) });
|
|
9297
9670
|
const linkedFile = join6(target, ".jioogen", "project.json");
|
|
9298
9671
|
if (options.json === true) {
|
|
9299
|
-
console.log(
|
|
9300
|
-
JSON.stringify(
|
|
9301
|
-
{ projectId, target, apiUrl, linkedFile, skills: skills.written.length },
|
|
9302
|
-
null,
|
|
9303
|
-
2
|
|
9304
|
-
)
|
|
9305
|
-
);
|
|
9672
|
+
console.log(JSON.stringify({ projectId, target, apiUrl, linkedFile, skills }, null, 2));
|
|
9306
9673
|
return;
|
|
9307
9674
|
}
|
|
9308
9675
|
console.log(`Project ${projectId} ter-bind ke ${target}.`);
|
|
9309
9676
|
console.log(`File link: ${linkedFile} (API ${apiUrl}).`);
|
|
9310
9677
|
console.log(`Skills eksekusi terpasang: ${skills.written.length} file.`);
|
|
9678
|
+
printSkillPacks(skills.packs, skills.missing);
|
|
9311
9679
|
}
|
|
9312
9680
|
async function skillsInstallCommand(options) {
|
|
9313
|
-
const
|
|
9314
|
-
const
|
|
9315
|
-
const skills = await installAgentSkills(bundleDir);
|
|
9681
|
+
const bundleDir = await resolveSkillsBundleDir(options);
|
|
9682
|
+
const skills = await installAgentSkills(bundleDir, { noSkills: skillsDisabled(options) });
|
|
9316
9683
|
if (options.json === true) {
|
|
9317
9684
|
console.log(JSON.stringify({ bundleDir, ...skills }, null, 2));
|
|
9318
9685
|
return;
|
|
@@ -9320,6 +9687,90 @@ async function skillsInstallCommand(options) {
|
|
|
9320
9687
|
console.log(`Skills eksekusi terpasang di ${bundleDir}:`);
|
|
9321
9688
|
for (const file of skills.written) console.log(`- ${file}`);
|
|
9322
9689
|
for (const file of skills.preserved) console.log(`- ${file} (dilewati: milik user)`);
|
|
9690
|
+
printSkillPacks(skills.packs, skills.missing);
|
|
9691
|
+
}
|
|
9692
|
+
async function skillsListCommand(options) {
|
|
9693
|
+
const bundleDir = await resolveSkillsBundleDir(options);
|
|
9694
|
+
const manifest = await readHarnessManifest(bundleDir);
|
|
9695
|
+
if (options.json === true) {
|
|
9696
|
+
console.log(JSON.stringify({ bundleDir, harness: manifest }, null, 2));
|
|
9697
|
+
return;
|
|
9698
|
+
}
|
|
9699
|
+
if (manifest === null) {
|
|
9700
|
+
console.log(`Belum ada skill pack terpasang di ${bundleDir}.`);
|
|
9701
|
+
return;
|
|
9702
|
+
}
|
|
9703
|
+
console.log(`Bundle: ${bundleDir}`);
|
|
9704
|
+
console.log(`Product: ${manifest.productKind} | updated: ${manifest.updatedAt}`);
|
|
9705
|
+
for (const pack of manifest.packs) {
|
|
9706
|
+
console.log(`${pack.name}:`);
|
|
9707
|
+
for (const skill of pack.skills) {
|
|
9708
|
+
console.log(`- ${skill.name} (installed, ${skill.sha256.slice(0, 12)})`);
|
|
9709
|
+
}
|
|
9710
|
+
for (const name of pack.missing) console.log(`- ${name} (missing)`);
|
|
9711
|
+
if (pack.skills.length === 0 && pack.missing.length === 0) console.log("- (tidak ada skill)");
|
|
9712
|
+
}
|
|
9713
|
+
}
|
|
9714
|
+
async function skillsAddCommand(packArgument, options) {
|
|
9715
|
+
const packNames = parseSkillPackArgument(packArgument);
|
|
9716
|
+
if (packNames === null) {
|
|
9717
|
+
throw new UsageError("Pack tidak dikenal. Pilih: baseline | ui-quality | backend-rules | all.");
|
|
9718
|
+
}
|
|
9719
|
+
const bundleDir = await resolveSkillsBundleDir(options);
|
|
9720
|
+
const result = await addSkillPacks(bundleDir, packNames);
|
|
9721
|
+
if (options.json === true) {
|
|
9722
|
+
console.log(JSON.stringify({ bundleDir, ...result }, null, 2));
|
|
9723
|
+
return;
|
|
9724
|
+
}
|
|
9725
|
+
console.log(`Skill pack ditambahkan ke ${bundleDir}: ${result.added.join(", ")}`);
|
|
9726
|
+
printSkillPacks(result.packs, result.missing);
|
|
9727
|
+
}
|
|
9728
|
+
async function skillsUpdateCommand(options) {
|
|
9729
|
+
const bundleDir = await resolveSkillsBundleDir(options);
|
|
9730
|
+
const result = await updateSkillPacks(bundleDir);
|
|
9731
|
+
if (result === null) {
|
|
9732
|
+
if (options.json === true) {
|
|
9733
|
+
console.log(JSON.stringify({ bundleDir, harness: null }, null, 2));
|
|
9734
|
+
return;
|
|
9735
|
+
}
|
|
9736
|
+
console.log(`Belum ada skill pack tercatat di ${bundleDir}.`);
|
|
9737
|
+
return;
|
|
9738
|
+
}
|
|
9739
|
+
if (options.json === true) {
|
|
9740
|
+
console.log(JSON.stringify({ bundleDir, ...result }, null, 2));
|
|
9741
|
+
return;
|
|
9742
|
+
}
|
|
9743
|
+
console.log(`Skill pack diperbarui di ${bundleDir}.`);
|
|
9744
|
+
printSkillPacks(result.packs, result.missing);
|
|
9745
|
+
}
|
|
9746
|
+
async function skillsRemoveCommand(packArgument, options) {
|
|
9747
|
+
const packNames = parseSkillPackArgument(packArgument);
|
|
9748
|
+
if (packNames === null || packNames.length !== 1) {
|
|
9749
|
+
throw new UsageError("Pack tidak dikenal. Pilih: baseline | ui-quality | backend-rules.");
|
|
9750
|
+
}
|
|
9751
|
+
const pack = packNames[0];
|
|
9752
|
+
const bundleDir = await resolveSkillsBundleDir(options);
|
|
9753
|
+
const result = await removeSkillPack(bundleDir, pack);
|
|
9754
|
+
if (options.json === true) {
|
|
9755
|
+
console.log(JSON.stringify({ bundleDir, pack, ...result }, null, 2));
|
|
9756
|
+
return;
|
|
9757
|
+
}
|
|
9758
|
+
console.log(`Skill pack ${pack} dihapus dari ${bundleDir}: ${result.removed.length} file.`);
|
|
9759
|
+
for (const file of result.preserved) {
|
|
9760
|
+
console.log(`- ${file} (dilewati: milik user / sudah berubah)`);
|
|
9761
|
+
}
|
|
9762
|
+
}
|
|
9763
|
+
async function resolveSkillsBundleDir(options) {
|
|
9764
|
+
const link = await findProjectLink(options.dir ?? process.cwd());
|
|
9765
|
+
return resolve4(link?.bundleDir ?? options.dir ?? process.cwd());
|
|
9766
|
+
}
|
|
9767
|
+
function printSkillPacks(packs, missing) {
|
|
9768
|
+
if (packs.length === 0 && missing.length === 0) return;
|
|
9769
|
+
const installed = packs.filter((pack) => pack.skills.length > 0).map((pack) => `${pack.name} (${pack.skills.map((skill) => skill.name).join(", ")})`);
|
|
9770
|
+
if (installed.length > 0) console.log(`Skill pack: ${installed.join("; ")}.`);
|
|
9771
|
+
if (missing.length > 0) {
|
|
9772
|
+
console.warn(`Peringatan: skill tidak ditemukan (dilewati): ${missing.join(", ")}.`);
|
|
9773
|
+
}
|
|
9323
9774
|
}
|
|
9324
9775
|
async function checkCommand(options) {
|
|
9325
9776
|
const link = await findProjectLink(options.dir ?? process.cwd());
|
|
@@ -9348,15 +9799,19 @@ async function handoffCommand(options) {
|
|
|
9348
9799
|
}
|
|
9349
9800
|
}
|
|
9350
9801
|
async function taskNextCommand(options) {
|
|
9351
|
-
const { projectId, definitions, written, next } = await withLocalState(
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
|
|
9359
|
-
|
|
9802
|
+
const { projectId, bundleDir, definitions, written, next } = await withLocalState(
|
|
9803
|
+
options,
|
|
9804
|
+
async (local) => {
|
|
9805
|
+
const written2 = await writeState(local.bundleDir, local.state);
|
|
9806
|
+
return {
|
|
9807
|
+
projectId: local.projectId,
|
|
9808
|
+
bundleDir: local.bundleDir,
|
|
9809
|
+
definitions: local.definitions,
|
|
9810
|
+
written: written2,
|
|
9811
|
+
next: selectNext(local.definitions, written2.tasks)
|
|
9812
|
+
};
|
|
9813
|
+
}
|
|
9814
|
+
);
|
|
9360
9815
|
if (next === null) {
|
|
9361
9816
|
if (options.json === true) {
|
|
9362
9817
|
console.log(JSON.stringify({ done: true, projectId }, null, 2));
|
|
@@ -9373,7 +9828,12 @@ async function taskNextCommand(options) {
|
|
|
9373
9828
|
return;
|
|
9374
9829
|
}
|
|
9375
9830
|
const awaitingCheckpoint = written.current?.awaitingCheckpoint === true;
|
|
9376
|
-
const
|
|
9831
|
+
const manifest = await readHarnessManifest(bundleDir);
|
|
9832
|
+
const context = manifest === null ? void 0 : {
|
|
9833
|
+
productKind: manifest.productKind,
|
|
9834
|
+
skills: manifest.packs.flatMap((pack) => pack.skills.map((skill) => skill.name))
|
|
9835
|
+
};
|
|
9836
|
+
const instruction = renderActiveTask(next.task, context);
|
|
9377
9837
|
if (options.json === true) {
|
|
9378
9838
|
console.log(
|
|
9379
9839
|
JSON.stringify(
|
|
@@ -9647,9 +10107,9 @@ async function assertPullDefaultDir(cwd, dirOption) {
|
|
|
9647
10107
|
async function assertPullTarget(target, projectId, force = false) {
|
|
9648
10108
|
let entries;
|
|
9649
10109
|
try {
|
|
9650
|
-
entries = await
|
|
10110
|
+
entries = await readdir4(target);
|
|
9651
10111
|
} catch (error) {
|
|
9652
|
-
if (
|
|
10112
|
+
if (isMissingFile5(error)) return;
|
|
9653
10113
|
throw error;
|
|
9654
10114
|
}
|
|
9655
10115
|
if (entries.length === 0) return;
|
|
@@ -9684,16 +10144,16 @@ async function hasBundleMarkers(target) {
|
|
|
9684
10144
|
await readFile6(join6(target, "PRD.md"));
|
|
9685
10145
|
return true;
|
|
9686
10146
|
} catch (error) {
|
|
9687
|
-
if (!
|
|
10147
|
+
if (!isMissingFile5(error)) throw error;
|
|
9688
10148
|
}
|
|
9689
10149
|
return hasTaskMarkdown(join6(target, "tasks"));
|
|
9690
10150
|
}
|
|
9691
10151
|
async function hasTaskMarkdown(directory) {
|
|
9692
10152
|
let names;
|
|
9693
10153
|
try {
|
|
9694
|
-
names = await
|
|
10154
|
+
names = await readdir4(directory);
|
|
9695
10155
|
} catch (error) {
|
|
9696
|
-
if (
|
|
10156
|
+
if (isMissingFile5(error)) return false;
|
|
9697
10157
|
throw error;
|
|
9698
10158
|
}
|
|
9699
10159
|
return names.some((name) => name.toLowerCase().endsWith(".md"));
|
|
@@ -9755,12 +10215,12 @@ function envFirst(primary, legacy) {
|
|
|
9755
10215
|
const fallback = process.env[legacy]?.trim();
|
|
9756
10216
|
return fallback !== void 0 && fallback.length > 0 ? fallback : void 0;
|
|
9757
10217
|
}
|
|
9758
|
-
function
|
|
10218
|
+
function isMissingFile5(error) {
|
|
9759
10219
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
9760
10220
|
}
|
|
9761
10221
|
|
|
9762
10222
|
// src/index.ts
|
|
9763
|
-
var VERSION = "0.2.
|
|
10223
|
+
var VERSION = "0.2.4";
|
|
9764
10224
|
var USAGE = `jioogen \u2014 CLI JiooGen (task selection lokal, server read-only)
|
|
9765
10225
|
|
|
9766
10226
|
Usage:
|
|
@@ -9769,9 +10229,13 @@ Usage:
|
|
|
9769
10229
|
jioogen logout
|
|
9770
10230
|
jioogen whoami
|
|
9771
10231
|
jioogen projects
|
|
9772
|
-
jioogen pull <projectId> [--dir <dir>] [--force]
|
|
9773
|
-
jioogen link <projectId> [--dir <dir>]
|
|
9774
|
-
jioogen skills install [--dir <dir>]
|
|
10232
|
+
jioogen pull <projectId> [--dir <dir>] [--force] [--no-skills]
|
|
10233
|
+
jioogen link <projectId> [--dir <dir>] [--no-skills]
|
|
10234
|
+
jioogen skills install [--dir <dir>] [--no-skills]
|
|
10235
|
+
jioogen skills list [--dir <dir>] [--json]
|
|
10236
|
+
jioogen skills add <baseline|ui-quality|backend-rules|all> [--dir <dir>]
|
|
10237
|
+
jioogen skills update [--dir <dir>]
|
|
10238
|
+
jioogen skills remove <baseline|ui-quality|backend-rules> [--dir <dir>]
|
|
9775
10239
|
jioogen check [--dir <dir>] [--json]
|
|
9776
10240
|
jioogen handoff [--project <id>] [--json]
|
|
9777
10241
|
jioogen task next [--json]
|
|
@@ -9789,6 +10253,7 @@ Global options:
|
|
|
9789
10253
|
--api <url> Base URL JiooGen (env JIOOGEN_API / JIOOCODE_API / tersimpan)
|
|
9790
10254
|
--project <id> Override binding project
|
|
9791
10255
|
--dir <dir> Override direktori bundle
|
|
10256
|
+
--no-skills Jangan pasang skill pack (atau env JIOOGEN_NO_SKILLS=1)
|
|
9792
10257
|
-h, --help Tampilkan bantuan
|
|
9793
10258
|
-v, --version Tampilkan versi
|
|
9794
10259
|
`;
|
|
@@ -9809,6 +10274,7 @@ async function main() {
|
|
|
9809
10274
|
dir: { type: "string" },
|
|
9810
10275
|
remote: { type: "boolean" },
|
|
9811
10276
|
force: { type: "boolean" },
|
|
10277
|
+
"no-skills": { type: "boolean" },
|
|
9812
10278
|
files: { type: "string" },
|
|
9813
10279
|
note: { type: "string" },
|
|
9814
10280
|
json: { type: "boolean" },
|
|
@@ -9835,6 +10301,7 @@ async function main() {
|
|
|
9835
10301
|
dir: text(raw.dir),
|
|
9836
10302
|
remote: flag(raw.remote),
|
|
9837
10303
|
force: flag(raw.force),
|
|
10304
|
+
noSkills: flag(raw["no-skills"]),
|
|
9838
10305
|
files: text(raw.files),
|
|
9839
10306
|
note: text(raw.note),
|
|
9840
10307
|
json: flag(raw.json)
|
|
@@ -9925,8 +10392,30 @@ async function dispatchSkills(rest, options) {
|
|
|
9925
10392
|
case "install":
|
|
9926
10393
|
await skillsInstallCommand(options);
|
|
9927
10394
|
return;
|
|
10395
|
+
case "list":
|
|
10396
|
+
await skillsListCommand(options);
|
|
10397
|
+
return;
|
|
10398
|
+
case "add": {
|
|
10399
|
+
const pack = rest[1];
|
|
10400
|
+
if (pack === void 0) {
|
|
10401
|
+
throw new UsageError("`jioogen skills add` butuh <baseline|ui-quality|backend-rules|all>.");
|
|
10402
|
+
}
|
|
10403
|
+
await skillsAddCommand(pack, options);
|
|
10404
|
+
return;
|
|
10405
|
+
}
|
|
10406
|
+
case "update":
|
|
10407
|
+
await skillsUpdateCommand(options);
|
|
10408
|
+
return;
|
|
10409
|
+
case "remove": {
|
|
10410
|
+
const pack = rest[1];
|
|
10411
|
+
if (pack === void 0) {
|
|
10412
|
+
throw new UsageError("`jioogen skills remove` butuh <baseline|ui-quality|backend-rules>.");
|
|
10413
|
+
}
|
|
10414
|
+
await skillsRemoveCommand(pack, options);
|
|
10415
|
+
return;
|
|
10416
|
+
}
|
|
9928
10417
|
default:
|
|
9929
|
-
throw new UsageError("Subcommand skills: install");
|
|
10418
|
+
throw new UsageError("Subcommand skills: install | list | add | update | remove");
|
|
9930
10419
|
}
|
|
9931
10420
|
}
|
|
9932
10421
|
async function dispatchTask(rest, options) {
|