prjct-cli 1.17.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +101 -0
- package/core/__tests__/domain/change-propagator.test.ts +100 -0
- package/core/__tests__/domain/file-hasher.test.ts +146 -0
- package/core/__tests__/storage/archive-storage.test.ts +455 -0
- package/core/commands/analysis.ts +9 -2
- package/core/commands/command-data.ts +3 -1
- package/core/commands/commands.ts +1 -0
- package/core/domain/change-propagator.ts +162 -0
- package/core/domain/file-hasher.ts +296 -0
- package/core/index.ts +1 -0
- package/core/schemas/ideas.ts +1 -1
- package/core/services/memory-service.ts +42 -0
- package/core/services/sync-service.ts +178 -13
- package/core/services/watch-service.ts +1 -1
- package/core/storage/archive-storage.ts +205 -0
- package/core/storage/database.ts +24 -0
- package/core/storage/ideas-storage.ts +54 -2
- package/core/storage/index.ts +2 -0
- package/core/storage/queue-storage.ts +43 -1
- package/core/storage/shipped-storage.ts +45 -1
- package/core/storage/state-storage.ts +16 -2
- package/core/types/index.ts +1 -0
- package/core/types/project-sync.ts +20 -0
- package/core/types/storage.ts +1 -1
- package/dist/bin/prjct.mjs +1174 -474
- package/package.json +1 -1
package/dist/bin/prjct.mjs
CHANGED
|
@@ -16,10 +16,10 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
16
16
|
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
17
17
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
18
18
|
});
|
|
19
|
-
var __glob = (map) => (
|
|
20
|
-
var fn = map[
|
|
19
|
+
var __glob = (map) => (path73) => {
|
|
20
|
+
var fn = map[path73];
|
|
21
21
|
if (fn) return fn();
|
|
22
|
-
throw new Error("Module not found in bundle: " +
|
|
22
|
+
throw new Error("Module not found in bundle: " + path73);
|
|
23
23
|
};
|
|
24
24
|
var __esm = (fn, res) => function __init() {
|
|
25
25
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
@@ -4277,6 +4277,30 @@ var init_database = __esm({
|
|
|
4277
4277
|
);
|
|
4278
4278
|
`);
|
|
4279
4279
|
}, "up")
|
|
4280
|
+
},
|
|
4281
|
+
{
|
|
4282
|
+
version: 2,
|
|
4283
|
+
name: "archives-table",
|
|
4284
|
+
up: /* @__PURE__ */ __name((db) => {
|
|
4285
|
+
db.run(`
|
|
4286
|
+
-- =======================================================================
|
|
4287
|
+
-- Archives: Stale data moved out of active storage (PRJ-267)
|
|
4288
|
+
-- =======================================================================
|
|
4289
|
+
CREATE TABLE archives (
|
|
4290
|
+
id TEXT PRIMARY KEY,
|
|
4291
|
+
entity_type TEXT NOT NULL,
|
|
4292
|
+
entity_id TEXT NOT NULL,
|
|
4293
|
+
entity_data TEXT NOT NULL,
|
|
4294
|
+
summary TEXT,
|
|
4295
|
+
archived_at TEXT NOT NULL,
|
|
4296
|
+
reason TEXT NOT NULL
|
|
4297
|
+
);
|
|
4298
|
+
|
|
4299
|
+
CREATE INDEX idx_archives_entity_type ON archives(entity_type);
|
|
4300
|
+
CREATE INDEX idx_archives_archived_at ON archives(archived_at);
|
|
4301
|
+
CREATE INDEX idx_archives_entity_id ON archives(entity_id);
|
|
4302
|
+
`);
|
|
4303
|
+
}, "up")
|
|
4280
4304
|
}
|
|
4281
4305
|
];
|
|
4282
4306
|
PrjctDatabase = class {
|
|
@@ -6576,11 +6600,11 @@ async function runSignaturesTool(args2, projectPath) {
|
|
|
6576
6600
|
}
|
|
6577
6601
|
};
|
|
6578
6602
|
}
|
|
6579
|
-
const
|
|
6580
|
-
const
|
|
6581
|
-
const fullPath =
|
|
6603
|
+
const fs61 = await import("node:fs/promises");
|
|
6604
|
+
const path73 = await import("node:path");
|
|
6605
|
+
const fullPath = path73.isAbsolute(filePath) ? filePath : path73.join(projectPath, filePath);
|
|
6582
6606
|
try {
|
|
6583
|
-
const stat = await
|
|
6607
|
+
const stat = await fs61.stat(fullPath);
|
|
6584
6608
|
if (stat.isDirectory()) {
|
|
6585
6609
|
const results = await extractDirectorySignatures(filePath, projectPath, {
|
|
6586
6610
|
recursive: args2.includes("--recursive") || args2.includes("-r")
|
|
@@ -6647,11 +6671,11 @@ async function runSummaryTool(args2, projectPath) {
|
|
|
6647
6671
|
}
|
|
6648
6672
|
};
|
|
6649
6673
|
}
|
|
6650
|
-
const
|
|
6651
|
-
const
|
|
6652
|
-
const fullPath =
|
|
6674
|
+
const fs61 = await import("node:fs/promises");
|
|
6675
|
+
const path73 = await import("node:path");
|
|
6676
|
+
const fullPath = path73.isAbsolute(targetPath) ? targetPath : path73.join(projectPath, targetPath);
|
|
6653
6677
|
try {
|
|
6654
|
-
const stat = await
|
|
6678
|
+
const stat = await fs61.stat(fullPath);
|
|
6655
6679
|
if (stat.isDirectory()) {
|
|
6656
6680
|
const results = await summarizeDirectory(targetPath, projectPath, {
|
|
6657
6681
|
recursive: args2.includes("--recursive") || args2.includes("-r")
|
|
@@ -9716,7 +9740,7 @@ var init_ideas = __esm({
|
|
|
9716
9740
|
"core/schemas/ideas.ts"() {
|
|
9717
9741
|
"use strict";
|
|
9718
9742
|
IdeaPrioritySchema = z6.enum(["low", "medium", "high"]);
|
|
9719
|
-
IdeaStatusSchema = z6.enum(["pending", "converted", "completed", "archived"]);
|
|
9743
|
+
IdeaStatusSchema = z6.enum(["pending", "converted", "completed", "archived", "dormant"]);
|
|
9720
9744
|
ImpactLevelSchema = z6.enum(["high", "medium", "low"]);
|
|
9721
9745
|
ImpactEffortSchema = z6.object({
|
|
9722
9746
|
impact: ImpactLevelSchema,
|
|
@@ -12726,6 +12750,147 @@ var init_analysis_storage = __esm({
|
|
|
12726
12750
|
}
|
|
12727
12751
|
});
|
|
12728
12752
|
|
|
12753
|
+
// core/storage/archive-storage.ts
|
|
12754
|
+
var ARCHIVE_POLICIES, ArchiveStorage, archiveStorage;
|
|
12755
|
+
var init_archive_storage = __esm({
|
|
12756
|
+
"core/storage/archive-storage.ts"() {
|
|
12757
|
+
"use strict";
|
|
12758
|
+
init_schemas2();
|
|
12759
|
+
init_date_helper();
|
|
12760
|
+
init_database();
|
|
12761
|
+
ARCHIVE_POLICIES = {
|
|
12762
|
+
SHIPPED_RETENTION_DAYS: 90,
|
|
12763
|
+
IDEA_DORMANT_DAYS: 180,
|
|
12764
|
+
QUEUE_COMPLETED_DAYS: 7,
|
|
12765
|
+
PAUSED_TASK_DAYS: 30,
|
|
12766
|
+
MEMORY_MAX_ENTRIES: 500
|
|
12767
|
+
};
|
|
12768
|
+
ArchiveStorage = class {
|
|
12769
|
+
static {
|
|
12770
|
+
__name(this, "ArchiveStorage");
|
|
12771
|
+
}
|
|
12772
|
+
/**
|
|
12773
|
+
* Archive a single item
|
|
12774
|
+
*/
|
|
12775
|
+
archive(projectId, item) {
|
|
12776
|
+
const id = generateUUID();
|
|
12777
|
+
const now = getTimestamp();
|
|
12778
|
+
prjctDb.run(
|
|
12779
|
+
projectId,
|
|
12780
|
+
"INSERT INTO archives (id, entity_type, entity_id, entity_data, summary, archived_at, reason) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
12781
|
+
id,
|
|
12782
|
+
item.entityType,
|
|
12783
|
+
item.entityId,
|
|
12784
|
+
JSON.stringify(item.entityData),
|
|
12785
|
+
item.summary ?? null,
|
|
12786
|
+
now,
|
|
12787
|
+
item.reason
|
|
12788
|
+
);
|
|
12789
|
+
return id;
|
|
12790
|
+
}
|
|
12791
|
+
/**
|
|
12792
|
+
* Archive multiple items in a transaction
|
|
12793
|
+
*/
|
|
12794
|
+
archiveMany(projectId, items) {
|
|
12795
|
+
if (items.length === 0) return 0;
|
|
12796
|
+
const now = getTimestamp();
|
|
12797
|
+
prjctDb.transaction(projectId, (db) => {
|
|
12798
|
+
const stmt = db.prepare(
|
|
12799
|
+
"INSERT INTO archives (id, entity_type, entity_id, entity_data, summary, archived_at, reason) VALUES (?, ?, ?, ?, ?, ?, ?)"
|
|
12800
|
+
);
|
|
12801
|
+
for (const item of items) {
|
|
12802
|
+
stmt.run(
|
|
12803
|
+
generateUUID(),
|
|
12804
|
+
item.entityType,
|
|
12805
|
+
item.entityId,
|
|
12806
|
+
JSON.stringify(item.entityData),
|
|
12807
|
+
item.summary ?? null,
|
|
12808
|
+
now,
|
|
12809
|
+
item.reason
|
|
12810
|
+
);
|
|
12811
|
+
}
|
|
12812
|
+
});
|
|
12813
|
+
return items.length;
|
|
12814
|
+
}
|
|
12815
|
+
/**
|
|
12816
|
+
* Query archived items by type
|
|
12817
|
+
*/
|
|
12818
|
+
getArchived(projectId, entityType, limit = 50) {
|
|
12819
|
+
if (entityType) {
|
|
12820
|
+
return prjctDb.query(
|
|
12821
|
+
projectId,
|
|
12822
|
+
"SELECT * FROM archives WHERE entity_type = ? ORDER BY archived_at DESC LIMIT ?",
|
|
12823
|
+
entityType,
|
|
12824
|
+
limit
|
|
12825
|
+
);
|
|
12826
|
+
}
|
|
12827
|
+
return prjctDb.query(
|
|
12828
|
+
projectId,
|
|
12829
|
+
"SELECT * FROM archives ORDER BY archived_at DESC LIMIT ?",
|
|
12830
|
+
limit
|
|
12831
|
+
);
|
|
12832
|
+
}
|
|
12833
|
+
/**
|
|
12834
|
+
* Get count of archived items by type
|
|
12835
|
+
*/
|
|
12836
|
+
getStats(projectId) {
|
|
12837
|
+
const rows = prjctDb.query(
|
|
12838
|
+
projectId,
|
|
12839
|
+
"SELECT entity_type, COUNT(*) as count FROM archives GROUP BY entity_type"
|
|
12840
|
+
);
|
|
12841
|
+
const stats = {
|
|
12842
|
+
shipped: 0,
|
|
12843
|
+
idea: 0,
|
|
12844
|
+
queue_task: 0,
|
|
12845
|
+
paused_task: 0,
|
|
12846
|
+
memory_entry: 0,
|
|
12847
|
+
total: 0
|
|
12848
|
+
};
|
|
12849
|
+
for (const row of rows) {
|
|
12850
|
+
const type = row.entity_type;
|
|
12851
|
+
if (type in stats) {
|
|
12852
|
+
stats[type] = row.count;
|
|
12853
|
+
}
|
|
12854
|
+
stats.total += row.count;
|
|
12855
|
+
}
|
|
12856
|
+
return stats;
|
|
12857
|
+
}
|
|
12858
|
+
/**
|
|
12859
|
+
* Restore an item from archive (removes from archive table)
|
|
12860
|
+
* Returns the entity data for the caller to re-insert into active storage.
|
|
12861
|
+
*/
|
|
12862
|
+
restore(projectId, archiveId) {
|
|
12863
|
+
const record = prjctDb.get(
|
|
12864
|
+
projectId,
|
|
12865
|
+
"SELECT * FROM archives WHERE id = ?",
|
|
12866
|
+
archiveId
|
|
12867
|
+
);
|
|
12868
|
+
if (!record) return null;
|
|
12869
|
+
prjctDb.run(projectId, "DELETE FROM archives WHERE id = ?", archiveId);
|
|
12870
|
+
return JSON.parse(record.entity_data);
|
|
12871
|
+
}
|
|
12872
|
+
/**
|
|
12873
|
+
* Permanently delete archives older than N days
|
|
12874
|
+
*/
|
|
12875
|
+
pruneOldArchives(projectId, olderThanDays) {
|
|
12876
|
+
const threshold = new Date(Date.now() - olderThanDays * 24 * 60 * 60 * 1e3).toISOString();
|
|
12877
|
+
const before = this.getTotalCount(projectId);
|
|
12878
|
+
prjctDb.run(projectId, "DELETE FROM archives WHERE archived_at < ?", threshold);
|
|
12879
|
+
const after = this.getTotalCount(projectId);
|
|
12880
|
+
return before - after;
|
|
12881
|
+
}
|
|
12882
|
+
/**
|
|
12883
|
+
* Get total count of archived items
|
|
12884
|
+
*/
|
|
12885
|
+
getTotalCount(projectId) {
|
|
12886
|
+
const row = prjctDb.get(projectId, "SELECT COUNT(*) as count FROM archives");
|
|
12887
|
+
return row?.count ?? 0;
|
|
12888
|
+
}
|
|
12889
|
+
};
|
|
12890
|
+
archiveStorage = new ArchiveStorage();
|
|
12891
|
+
}
|
|
12892
|
+
});
|
|
12893
|
+
|
|
12729
12894
|
// core/storage/ideas-storage.ts
|
|
12730
12895
|
var IdeasStorage, ideasStorage;
|
|
12731
12896
|
var init_ideas_storage = __esm({
|
|
@@ -12734,6 +12899,7 @@ var init_ideas_storage = __esm({
|
|
|
12734
12899
|
init_schemas2();
|
|
12735
12900
|
init_ideas();
|
|
12736
12901
|
init_date_helper();
|
|
12902
|
+
init_archive_storage();
|
|
12737
12903
|
init_storage_manager();
|
|
12738
12904
|
IdeasStorage = class extends StorageManager {
|
|
12739
12905
|
static {
|
|
@@ -12762,6 +12928,7 @@ var init_ideas_storage = __esm({
|
|
|
12762
12928
|
const pending = data.ideas.filter((i) => i.status === "pending");
|
|
12763
12929
|
const converted = data.ideas.filter((i) => i.status === "converted");
|
|
12764
12930
|
const archived = data.ideas.filter((i) => i.status === "archived");
|
|
12931
|
+
const dormant = data.ideas.filter((i) => i.status === "dormant");
|
|
12765
12932
|
lines.push("## Brain Dump");
|
|
12766
12933
|
if (pending.length > 0) {
|
|
12767
12934
|
pending.forEach((idea) => {
|
|
@@ -12791,6 +12958,10 @@ var init_ideas_storage = __esm({
|
|
|
12791
12958
|
});
|
|
12792
12959
|
lines.push("");
|
|
12793
12960
|
}
|
|
12961
|
+
if (dormant.length > 0) {
|
|
12962
|
+
lines.push(`_${dormant.length} dormant idea(s) excluded from context_`);
|
|
12963
|
+
lines.push("");
|
|
12964
|
+
}
|
|
12794
12965
|
return lines.join("\n");
|
|
12795
12966
|
}
|
|
12796
12967
|
// =========== Domain Methods ===========
|
|
@@ -12926,6 +13097,40 @@ var init_ideas_storage = __esm({
|
|
|
12926
13097
|
}));
|
|
12927
13098
|
return { removed };
|
|
12928
13099
|
}
|
|
13100
|
+
/**
|
|
13101
|
+
* Mark pending ideas older than retention period as dormant (PRJ-267).
|
|
13102
|
+
* Dormant ideas are excluded from LLM context but remain queryable.
|
|
13103
|
+
* Returns count of newly dormant ideas.
|
|
13104
|
+
*/
|
|
13105
|
+
async markDormantIdeas(projectId) {
|
|
13106
|
+
const data = await this.read(projectId);
|
|
13107
|
+
const threshold = getDaysAgo(ARCHIVE_POLICIES.IDEA_DORMANT_DAYS);
|
|
13108
|
+
const stalePending = data.ideas.filter(
|
|
13109
|
+
(i) => i.status === "pending" && new Date(i.addedAt) < threshold
|
|
13110
|
+
);
|
|
13111
|
+
if (stalePending.length === 0) return 0;
|
|
13112
|
+
archiveStorage.archiveMany(
|
|
13113
|
+
projectId,
|
|
13114
|
+
stalePending.map((idea) => ({
|
|
13115
|
+
entityType: "idea",
|
|
13116
|
+
entityId: idea.id,
|
|
13117
|
+
entityData: idea,
|
|
13118
|
+
summary: idea.text,
|
|
13119
|
+
reason: "dormant"
|
|
13120
|
+
}))
|
|
13121
|
+
);
|
|
13122
|
+
const staleIds = new Set(stalePending.map((i) => i.id));
|
|
13123
|
+
await this.update(projectId, (d) => ({
|
|
13124
|
+
ideas: d.ideas.map(
|
|
13125
|
+
(i) => staleIds.has(i.id) ? { ...i, status: "dormant" } : i
|
|
13126
|
+
),
|
|
13127
|
+
lastUpdated: getTimestamp()
|
|
13128
|
+
}));
|
|
13129
|
+
await this.publishEvent(projectId, "ideas.dormant", {
|
|
13130
|
+
count: stalePending.length
|
|
13131
|
+
});
|
|
13132
|
+
return stalePending.length;
|
|
13133
|
+
}
|
|
12929
13134
|
};
|
|
12930
13135
|
ideasStorage = new IdeasStorage();
|
|
12931
13136
|
}
|
|
@@ -13764,6 +13969,7 @@ var init_queue_storage = __esm({
|
|
|
13764
13969
|
init_schemas2();
|
|
13765
13970
|
init_state();
|
|
13766
13971
|
init_date_helper();
|
|
13972
|
+
init_archive_storage();
|
|
13767
13973
|
init_storage_manager();
|
|
13768
13974
|
QueueStorage = class extends StorageManager {
|
|
13769
13975
|
static {
|
|
@@ -13969,6 +14175,38 @@ var init_queue_storage = __esm({
|
|
|
13969
14175
|
}));
|
|
13970
14176
|
return completedCount;
|
|
13971
14177
|
}
|
|
14178
|
+
/**
|
|
14179
|
+
* Remove completed tasks older than retention period (PRJ-267).
|
|
14180
|
+
* Archives them to SQLite before removal.
|
|
14181
|
+
* Returns count of removed tasks.
|
|
14182
|
+
*/
|
|
14183
|
+
async removeStaleCompleted(projectId) {
|
|
14184
|
+
const queue = await this.read(projectId);
|
|
14185
|
+
const threshold = getDaysAgo(ARCHIVE_POLICIES.QUEUE_COMPLETED_DAYS);
|
|
14186
|
+
const stale = queue.tasks.filter(
|
|
14187
|
+
(t) => t.completed && t.completedAt && new Date(t.completedAt) < threshold
|
|
14188
|
+
);
|
|
14189
|
+
if (stale.length === 0) return 0;
|
|
14190
|
+
archiveStorage.archiveMany(
|
|
14191
|
+
projectId,
|
|
14192
|
+
stale.map((t) => ({
|
|
14193
|
+
entityType: "queue_task",
|
|
14194
|
+
entityId: t.id,
|
|
14195
|
+
entityData: t,
|
|
14196
|
+
summary: t.description,
|
|
14197
|
+
reason: "age"
|
|
14198
|
+
}))
|
|
14199
|
+
);
|
|
14200
|
+
const staleIds = new Set(stale.map((t) => t.id));
|
|
14201
|
+
await this.update(projectId, (q) => ({
|
|
14202
|
+
tasks: q.tasks.filter((t) => !staleIds.has(t.id)),
|
|
14203
|
+
lastUpdated: getTimestamp()
|
|
14204
|
+
}));
|
|
14205
|
+
await this.publishEvent(projectId, "queue.stale_removed", {
|
|
14206
|
+
count: stale.length
|
|
14207
|
+
});
|
|
14208
|
+
return stale.length;
|
|
14209
|
+
}
|
|
13972
14210
|
/**
|
|
13973
14211
|
* Sort tasks by priority and section
|
|
13974
14212
|
*/
|
|
@@ -14005,6 +14243,7 @@ var init_shipped_storage = __esm({
|
|
|
14005
14243
|
init_schemas2();
|
|
14006
14244
|
init_shipped();
|
|
14007
14245
|
init_date_helper();
|
|
14246
|
+
init_archive_storage();
|
|
14008
14247
|
init_storage_manager();
|
|
14009
14248
|
ShippedStorage = class extends StorageManager {
|
|
14010
14249
|
static {
|
|
@@ -14154,6 +14393,39 @@ var init_shipped_storage = __esm({
|
|
|
14154
14393
|
period
|
|
14155
14394
|
};
|
|
14156
14395
|
}
|
|
14396
|
+
/**
|
|
14397
|
+
* Archive shipped features older than retention period (PRJ-267).
|
|
14398
|
+
* Moves old items to archive table, keeps 1-line summary in active storage.
|
|
14399
|
+
* Returns count of archived items.
|
|
14400
|
+
*/
|
|
14401
|
+
async archiveOldShipped(projectId) {
|
|
14402
|
+
const data = await this.read(projectId);
|
|
14403
|
+
const threshold = getDaysAgo(ARCHIVE_POLICIES.SHIPPED_RETENTION_DAYS);
|
|
14404
|
+
const stale = data.shipped.filter((s) => new Date(s.shippedAt) < threshold);
|
|
14405
|
+
if (stale.length === 0) return 0;
|
|
14406
|
+
archiveStorage.archiveMany(
|
|
14407
|
+
projectId,
|
|
14408
|
+
stale.map((s) => ({
|
|
14409
|
+
entityType: "shipped",
|
|
14410
|
+
entityId: s.id,
|
|
14411
|
+
entityData: s,
|
|
14412
|
+
summary: `${s.name} v${s.version}`,
|
|
14413
|
+
reason: "age"
|
|
14414
|
+
}))
|
|
14415
|
+
);
|
|
14416
|
+
const freshIds = new Set(
|
|
14417
|
+
data.shipped.filter((s) => new Date(s.shippedAt) >= threshold).map((s) => s.id)
|
|
14418
|
+
);
|
|
14419
|
+
await this.update(projectId, (d) => ({
|
|
14420
|
+
shipped: d.shipped.filter((s) => freshIds.has(s.id)),
|
|
14421
|
+
lastUpdated: getTimestamp()
|
|
14422
|
+
}));
|
|
14423
|
+
await this.publishEvent(projectId, "shipped.archived", {
|
|
14424
|
+
count: stale.length,
|
|
14425
|
+
oldestShippedAt: stale[stale.length - 1]?.shippedAt
|
|
14426
|
+
});
|
|
14427
|
+
return stale.length;
|
|
14428
|
+
}
|
|
14157
14429
|
};
|
|
14158
14430
|
shippedStorage = new ShippedStorage();
|
|
14159
14431
|
}
|
|
@@ -14557,6 +14829,7 @@ var init_state_storage = __esm({
|
|
|
14557
14829
|
init_date_helper();
|
|
14558
14830
|
init_markdown_builder();
|
|
14559
14831
|
init_state_machine();
|
|
14832
|
+
init_archive_storage();
|
|
14560
14833
|
init_storage_manager();
|
|
14561
14834
|
StateStorage = class extends StorageManager {
|
|
14562
14835
|
static {
|
|
@@ -14809,8 +15082,9 @@ var init_state_storage = __esm({
|
|
|
14809
15082
|
return pausedTasks.filter((t) => new Date(t.pausedAt).getTime() < threshold);
|
|
14810
15083
|
}
|
|
14811
15084
|
/**
|
|
14812
|
-
* Archive stale paused tasks (
|
|
14813
|
-
*
|
|
15085
|
+
* Archive stale paused tasks (PRJ-267).
|
|
15086
|
+
* Persists to archive table before removing from active storage.
|
|
15087
|
+
* Returns archived tasks.
|
|
14814
15088
|
*/
|
|
14815
15089
|
async archiveStalePausedTasks(projectId) {
|
|
14816
15090
|
const state = await this.read(projectId);
|
|
@@ -14819,6 +15093,16 @@ var init_state_storage = __esm({
|
|
|
14819
15093
|
const stale = pausedTasks.filter((t) => new Date(t.pausedAt).getTime() < threshold);
|
|
14820
15094
|
const fresh = pausedTasks.filter((t) => new Date(t.pausedAt).getTime() >= threshold);
|
|
14821
15095
|
if (stale.length === 0) return [];
|
|
15096
|
+
archiveStorage.archiveMany(
|
|
15097
|
+
projectId,
|
|
15098
|
+
stale.map((task) => ({
|
|
15099
|
+
entityType: "paused_task",
|
|
15100
|
+
entityId: task.id,
|
|
15101
|
+
entityData: task,
|
|
15102
|
+
summary: task.description,
|
|
15103
|
+
reason: "staleness"
|
|
15104
|
+
}))
|
|
15105
|
+
);
|
|
14822
15106
|
await this.update(projectId, (s) => ({
|
|
14823
15107
|
...s,
|
|
14824
15108
|
pausedTasks: fresh,
|
|
@@ -15434,6 +15718,7 @@ var init_storage2 = __esm({
|
|
|
15434
15718
|
"core/storage/index.ts"() {
|
|
15435
15719
|
"use strict";
|
|
15436
15720
|
init_analysis_storage();
|
|
15721
|
+
init_archive_storage();
|
|
15437
15722
|
init_database();
|
|
15438
15723
|
init_ideas_storage();
|
|
15439
15724
|
init_index_storage();
|
|
@@ -20764,6 +21049,7 @@ var init_memory_service = __esm({
|
|
|
20764
21049
|
"use strict";
|
|
20765
21050
|
init_config_manager();
|
|
20766
21051
|
init_path_manager();
|
|
21052
|
+
init_archive_storage();
|
|
20767
21053
|
init_fs();
|
|
20768
21054
|
init_date_helper();
|
|
20769
21055
|
init_jsonl_helper();
|
|
@@ -20859,6 +21145,39 @@ var init_memory_service = __esm({
|
|
|
20859
21145
|
return [];
|
|
20860
21146
|
}
|
|
20861
21147
|
}
|
|
21148
|
+
/**
|
|
21149
|
+
* Cap memory log at max entries (PRJ-267).
|
|
21150
|
+
* Moves overflow entries to archive table, keeps most recent entries.
|
|
21151
|
+
* Returns count of archived entries.
|
|
21152
|
+
*/
|
|
21153
|
+
async capEntries(projectId) {
|
|
21154
|
+
try {
|
|
21155
|
+
const memoryPath = path_manager_default.getFilePath(projectId, "memory", "context.jsonl");
|
|
21156
|
+
const entries = await readJsonLines(memoryPath);
|
|
21157
|
+
if (entries.length <= ARCHIVE_POLICIES.MEMORY_MAX_ENTRIES) {
|
|
21158
|
+
return 0;
|
|
21159
|
+
}
|
|
21160
|
+
const overflow = entries.slice(0, entries.length - ARCHIVE_POLICIES.MEMORY_MAX_ENTRIES);
|
|
21161
|
+
const kept = entries.slice(-ARCHIVE_POLICIES.MEMORY_MAX_ENTRIES);
|
|
21162
|
+
archiveStorage.archiveMany(
|
|
21163
|
+
projectId,
|
|
21164
|
+
overflow.map((entry, i) => ({
|
|
21165
|
+
entityType: "memory_entry",
|
|
21166
|
+
entityId: `memory-${entry.timestamp || i}`,
|
|
21167
|
+
entityData: entry,
|
|
21168
|
+
summary: entry.action,
|
|
21169
|
+
reason: "overflow"
|
|
21170
|
+
}))
|
|
21171
|
+
);
|
|
21172
|
+
await writeJsonLines(memoryPath, kept);
|
|
21173
|
+
return overflow.length;
|
|
21174
|
+
} catch (error) {
|
|
21175
|
+
if (!isNotFoundError(error)) {
|
|
21176
|
+
console.error(`Memory cap error: ${getErrorMessage2(error)}`);
|
|
21177
|
+
}
|
|
21178
|
+
return 0;
|
|
21179
|
+
}
|
|
21180
|
+
}
|
|
20862
21181
|
};
|
|
20863
21182
|
memoryService = new MemoryService();
|
|
20864
21183
|
}
|
|
@@ -21768,16 +22087,16 @@ var init_onboarding = __esm({
|
|
|
21768
22087
|
* Detect project type from file system
|
|
21769
22088
|
*/
|
|
21770
22089
|
async detectProjectType() {
|
|
21771
|
-
const
|
|
21772
|
-
const
|
|
22090
|
+
const fs61 = await import("node:fs/promises");
|
|
22091
|
+
const path73 = await import("node:path");
|
|
21773
22092
|
try {
|
|
21774
|
-
const files = await
|
|
22093
|
+
const files = await fs61.readdir(this.projectPath);
|
|
21775
22094
|
if (files.includes("turbo.json") || files.includes("lerna.json") || files.includes("nx.json")) {
|
|
21776
22095
|
return "monorepo";
|
|
21777
22096
|
}
|
|
21778
22097
|
if (files.includes("package.json")) {
|
|
21779
|
-
const pkgPath =
|
|
21780
|
-
const pkgContent = await
|
|
22098
|
+
const pkgPath = path73.join(this.projectPath, "package.json");
|
|
22099
|
+
const pkgContent = await fs61.readFile(pkgPath, "utf-8");
|
|
21781
22100
|
const pkg = JSON.parse(pkgContent);
|
|
21782
22101
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
21783
22102
|
if (pkg.bin) return "cli-tool";
|
|
@@ -21813,32 +22132,32 @@ var init_onboarding = __esm({
|
|
|
21813
22132
|
* Detect installed AI agents from config files
|
|
21814
22133
|
*/
|
|
21815
22134
|
async detectInstalledAgents() {
|
|
21816
|
-
const
|
|
21817
|
-
const
|
|
22135
|
+
const fs61 = await import("node:fs/promises");
|
|
22136
|
+
const path73 = await import("node:path");
|
|
21818
22137
|
const os22 = await import("node:os");
|
|
21819
22138
|
const agents = [];
|
|
21820
22139
|
try {
|
|
21821
|
-
await
|
|
22140
|
+
await fs61.access(path73.join(os22.homedir(), ".claude"));
|
|
21822
22141
|
agents.push("claude");
|
|
21823
22142
|
} catch {
|
|
21824
22143
|
}
|
|
21825
22144
|
try {
|
|
21826
|
-
await
|
|
22145
|
+
await fs61.access(path73.join(this.projectPath, ".cursorrules"));
|
|
21827
22146
|
agents.push("cursor");
|
|
21828
22147
|
} catch {
|
|
21829
22148
|
}
|
|
21830
22149
|
try {
|
|
21831
|
-
await
|
|
22150
|
+
await fs61.access(path73.join(this.projectPath, ".windsurfrules"));
|
|
21832
22151
|
agents.push("windsurf");
|
|
21833
22152
|
} catch {
|
|
21834
22153
|
}
|
|
21835
22154
|
try {
|
|
21836
|
-
await
|
|
22155
|
+
await fs61.access(path73.join(this.projectPath, ".github", "copilot-instructions.md"));
|
|
21837
22156
|
agents.push("copilot");
|
|
21838
22157
|
} catch {
|
|
21839
22158
|
}
|
|
21840
22159
|
try {
|
|
21841
|
-
await
|
|
22160
|
+
await fs61.access(path73.join(os22.homedir(), ".gemini"));
|
|
21842
22161
|
agents.push("gemini");
|
|
21843
22162
|
} catch {
|
|
21844
22163
|
}
|
|
@@ -21848,17 +22167,17 @@ var init_onboarding = __esm({
|
|
|
21848
22167
|
* Detect tech stack from project files
|
|
21849
22168
|
*/
|
|
21850
22169
|
async detectStack() {
|
|
21851
|
-
const
|
|
21852
|
-
const
|
|
22170
|
+
const fs61 = await import("node:fs/promises");
|
|
22171
|
+
const path73 = await import("node:path");
|
|
21853
22172
|
const stack = {
|
|
21854
22173
|
language: "Unknown",
|
|
21855
22174
|
technologies: []
|
|
21856
22175
|
};
|
|
21857
22176
|
try {
|
|
21858
|
-
const files = await
|
|
22177
|
+
const files = await fs61.readdir(this.projectPath);
|
|
21859
22178
|
if (files.includes("package.json")) {
|
|
21860
|
-
const pkgPath =
|
|
21861
|
-
const pkgContent = await
|
|
22179
|
+
const pkgPath = path73.join(this.projectPath, "package.json");
|
|
22180
|
+
const pkgContent = await fs61.readFile(pkgPath, "utf-8");
|
|
21862
22181
|
const pkg = JSON.parse(pkgContent);
|
|
21863
22182
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
21864
22183
|
stack.language = deps.typescript ? "TypeScript" : "JavaScript";
|
|
@@ -22984,7 +23303,10 @@ var init_analysis2 = __esm({
|
|
|
22984
23303
|
if (!isNonInteractive) {
|
|
22985
23304
|
output_default.spin("Analyzing changes...");
|
|
22986
23305
|
}
|
|
22987
|
-
const result2 = await syncService.sync(projectPath, {
|
|
23306
|
+
const result2 = await syncService.sync(projectPath, {
|
|
23307
|
+
aiTools: options.aiTools,
|
|
23308
|
+
full: options.full
|
|
23309
|
+
});
|
|
22988
23310
|
if (!result2.success) {
|
|
22989
23311
|
if (isNonInteractive) {
|
|
22990
23312
|
console.log(JSON.stringify({ success: false, error: result2.error || "Sync failed" }));
|
|
@@ -23098,7 +23420,10 @@ ${formatFullDiff(diff)}`);
|
|
|
23098
23420
|
return this.showSyncResult(result2, startTime);
|
|
23099
23421
|
}
|
|
23100
23422
|
output_default.spin("Syncing project...");
|
|
23101
|
-
const result = await syncService.sync(projectPath, {
|
|
23423
|
+
const result = await syncService.sync(projectPath, {
|
|
23424
|
+
aiTools: options.aiTools,
|
|
23425
|
+
full: options.full
|
|
23426
|
+
});
|
|
23102
23427
|
if (!result.success) {
|
|
23103
23428
|
output_default.fail(result.error || "Sync failed");
|
|
23104
23429
|
return { success: false, error: result.error };
|
|
@@ -24083,8 +24408,8 @@ Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}
|
|
|
24083
24408
|
const globalPath2 = path_manager_default.getGlobalProjectPath(projectId);
|
|
24084
24409
|
const specsPath2 = path45.join(globalPath2, "planning", "specs");
|
|
24085
24410
|
try {
|
|
24086
|
-
const
|
|
24087
|
-
const files = await
|
|
24411
|
+
const fs61 = await import("node:fs/promises");
|
|
24412
|
+
const files = await fs61.readdir(specsPath2);
|
|
24088
24413
|
const specs = files.filter((f) => f.endsWith(".md") && f !== ".gitkeep");
|
|
24089
24414
|
if (specs.length === 0) {
|
|
24090
24415
|
output_default.warn("no specs yet");
|
|
@@ -25283,109 +25608,6 @@ var init_bm25 = __esm({
|
|
|
25283
25608
|
}
|
|
25284
25609
|
});
|
|
25285
25610
|
|
|
25286
|
-
// core/domain/git-cochange.ts
|
|
25287
|
-
import { exec as execCallback7 } from "node:child_process";
|
|
25288
|
-
import { promisify as promisify15 } from "node:util";
|
|
25289
|
-
async function parseGitLog(projectPath, maxCommits = 100) {
|
|
25290
|
-
try {
|
|
25291
|
-
const { stdout } = await exec14(
|
|
25292
|
-
`git log --name-only --pretty=format:'---COMMIT---' -${maxCommits}`,
|
|
25293
|
-
{ cwd: projectPath, maxBuffer: 10 * 1024 * 1024 }
|
|
25294
|
-
);
|
|
25295
|
-
const commits = [];
|
|
25296
|
-
let currentFiles = null;
|
|
25297
|
-
for (const line of stdout.split("\n")) {
|
|
25298
|
-
const trimmed = line.trim();
|
|
25299
|
-
if (trimmed === "---COMMIT---") {
|
|
25300
|
-
if (currentFiles && currentFiles.size > 0 && currentFiles.size <= MAX_FILES_PER_COMMIT) {
|
|
25301
|
-
commits.push(currentFiles);
|
|
25302
|
-
}
|
|
25303
|
-
currentFiles = /* @__PURE__ */ new Set();
|
|
25304
|
-
} else if (trimmed && currentFiles) {
|
|
25305
|
-
if (isSourceFile(trimmed)) {
|
|
25306
|
-
currentFiles.add(trimmed);
|
|
25307
|
-
}
|
|
25308
|
-
}
|
|
25309
|
-
}
|
|
25310
|
-
if (currentFiles && currentFiles.size > 0 && currentFiles.size <= MAX_FILES_PER_COMMIT) {
|
|
25311
|
-
commits.push(currentFiles);
|
|
25312
|
-
}
|
|
25313
|
-
return commits;
|
|
25314
|
-
} catch {
|
|
25315
|
-
return [];
|
|
25316
|
-
}
|
|
25317
|
-
}
|
|
25318
|
-
function isSourceFile(filePath) {
|
|
25319
|
-
const sourceExtensions = /\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|cs|rb|php|vue|svelte)$/i;
|
|
25320
|
-
return sourceExtensions.test(filePath) && !filePath.includes("node_modules/");
|
|
25321
|
-
}
|
|
25322
|
-
async function buildMatrix(projectPath, maxCommits = 100) {
|
|
25323
|
-
const commitSets = await parseGitLog(projectPath, maxCommits);
|
|
25324
|
-
const fileCommitCount = /* @__PURE__ */ new Map();
|
|
25325
|
-
const pairCount = /* @__PURE__ */ new Map();
|
|
25326
|
-
for (const files of commitSets) {
|
|
25327
|
-
const fileArray = Array.from(files);
|
|
25328
|
-
for (const file of fileArray) {
|
|
25329
|
-
fileCommitCount.set(file, (fileCommitCount.get(file) || 0) + 1);
|
|
25330
|
-
}
|
|
25331
|
-
for (let i = 0; i < fileArray.length; i++) {
|
|
25332
|
-
for (let j = i + 1; j < fileArray.length; j++) {
|
|
25333
|
-
const key = pairKey(fileArray[i], fileArray[j]);
|
|
25334
|
-
pairCount.set(key, (pairCount.get(key) || 0) + 1);
|
|
25335
|
-
}
|
|
25336
|
-
}
|
|
25337
|
-
}
|
|
25338
|
-
const matrix = {};
|
|
25339
|
-
for (const [key, count] of pairCount) {
|
|
25340
|
-
const [fileA, fileB] = key.split("\0");
|
|
25341
|
-
const countA = fileCommitCount.get(fileA) || 0;
|
|
25342
|
-
const countB = fileCommitCount.get(fileB) || 0;
|
|
25343
|
-
if (countA < MIN_FILE_OCCURRENCES || countB < MIN_FILE_OCCURRENCES) continue;
|
|
25344
|
-
const unionCount = countA + countB - count;
|
|
25345
|
-
const similarity = unionCount > 0 ? count / unionCount : 0;
|
|
25346
|
-
if (similarity < MIN_SIMILARITY) continue;
|
|
25347
|
-
if (!matrix[fileA]) matrix[fileA] = {};
|
|
25348
|
-
if (!matrix[fileB]) matrix[fileB] = {};
|
|
25349
|
-
matrix[fileA][fileB] = similarity;
|
|
25350
|
-
matrix[fileB][fileA] = similarity;
|
|
25351
|
-
}
|
|
25352
|
-
return {
|
|
25353
|
-
matrix,
|
|
25354
|
-
commitsAnalyzed: commitSets.length,
|
|
25355
|
-
filesAnalyzed: fileCommitCount.size,
|
|
25356
|
-
builtAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
25357
|
-
};
|
|
25358
|
-
}
|
|
25359
|
-
function pairKey(a, b) {
|
|
25360
|
-
return a < b ? `${a}\0${b}` : `${b}\0${a}`;
|
|
25361
|
-
}
|
|
25362
|
-
function saveMatrix(projectId, index) {
|
|
25363
|
-
database_default.setDoc(projectId, INDEX_KEY2, index);
|
|
25364
|
-
}
|
|
25365
|
-
async function indexCoChanges(projectPath, projectId, maxCommits = 100) {
|
|
25366
|
-
const index = await buildMatrix(projectPath, maxCommits);
|
|
25367
|
-
saveMatrix(projectId, index);
|
|
25368
|
-
return index;
|
|
25369
|
-
}
|
|
25370
|
-
var exec14, MIN_SIMILARITY, MIN_FILE_OCCURRENCES, MAX_FILES_PER_COMMIT, INDEX_KEY2;
|
|
25371
|
-
var init_git_cochange = __esm({
|
|
25372
|
-
"core/domain/git-cochange.ts"() {
|
|
25373
|
-
"use strict";
|
|
25374
|
-
init_database();
|
|
25375
|
-
exec14 = promisify15(execCallback7);
|
|
25376
|
-
MIN_SIMILARITY = 0.1;
|
|
25377
|
-
MIN_FILE_OCCURRENCES = 2;
|
|
25378
|
-
MAX_FILES_PER_COMMIT = 30;
|
|
25379
|
-
__name(parseGitLog, "parseGitLog");
|
|
25380
|
-
__name(isSourceFile, "isSourceFile");
|
|
25381
|
-
__name(buildMatrix, "buildMatrix");
|
|
25382
|
-
__name(pairKey, "pairKey");
|
|
25383
|
-
INDEX_KEY2 = "cochange-index";
|
|
25384
|
-
__name(saveMatrix, "saveMatrix");
|
|
25385
|
-
__name(indexCoChanges, "indexCoChanges");
|
|
25386
|
-
}
|
|
25387
|
-
});
|
|
25388
|
-
|
|
25389
25611
|
// core/domain/import-graph.ts
|
|
25390
25612
|
import fs47 from "node:fs/promises";
|
|
25391
25613
|
import path50 from "node:path";
|
|
@@ -25483,14 +25705,17 @@ async function buildGraph(projectPath) {
|
|
|
25483
25705
|
};
|
|
25484
25706
|
}
|
|
25485
25707
|
function saveGraph(projectId, graph) {
|
|
25486
|
-
database_default.setDoc(projectId,
|
|
25708
|
+
database_default.setDoc(projectId, INDEX_KEY2, graph);
|
|
25709
|
+
}
|
|
25710
|
+
function loadGraph(projectId) {
|
|
25711
|
+
return database_default.getDoc(projectId, INDEX_KEY2);
|
|
25487
25712
|
}
|
|
25488
25713
|
async function indexImports(projectPath, projectId) {
|
|
25489
25714
|
const graph = await buildGraph(projectPath);
|
|
25490
25715
|
saveGraph(projectId, graph);
|
|
25491
25716
|
return graph;
|
|
25492
25717
|
}
|
|
25493
|
-
var INDEXABLE_EXTENSIONS2, SKIP_DIRS2, RESOLVE_EXTENSIONS, IMPORT_REGEX,
|
|
25718
|
+
var INDEXABLE_EXTENSIONS2, SKIP_DIRS2, RESOLVE_EXTENSIONS, IMPORT_REGEX, INDEX_KEY2;
|
|
25494
25719
|
var init_import_graph = __esm({
|
|
25495
25720
|
"core/domain/import-graph.ts"() {
|
|
25496
25721
|
"use strict";
|
|
@@ -25514,15 +25739,362 @@ var init_import_graph = __esm({
|
|
|
25514
25739
|
__name(resolveImport2, "resolveImport");
|
|
25515
25740
|
__name(listFiles3, "listFiles");
|
|
25516
25741
|
__name(buildGraph, "buildGraph");
|
|
25517
|
-
|
|
25742
|
+
INDEX_KEY2 = "import-graph";
|
|
25518
25743
|
__name(saveGraph, "saveGraph");
|
|
25744
|
+
__name(loadGraph, "loadGraph");
|
|
25519
25745
|
__name(indexImports, "indexImports");
|
|
25520
25746
|
}
|
|
25521
25747
|
});
|
|
25522
25748
|
|
|
25523
|
-
// core/
|
|
25749
|
+
// core/domain/change-propagator.ts
|
|
25750
|
+
function propagateChanges(diff, projectId) {
|
|
25751
|
+
const directlyChanged = [...diff.added, ...diff.modified];
|
|
25752
|
+
const directSet = new Set(directlyChanged);
|
|
25753
|
+
const affected = /* @__PURE__ */ new Set();
|
|
25754
|
+
const graph = loadGraph(projectId);
|
|
25755
|
+
if (graph) {
|
|
25756
|
+
for (const changedFile of directlyChanged) {
|
|
25757
|
+
const importers = graph.reverse[changedFile];
|
|
25758
|
+
if (importers) {
|
|
25759
|
+
for (const importer of importers) {
|
|
25760
|
+
if (!directSet.has(importer)) {
|
|
25761
|
+
affected.add(importer);
|
|
25762
|
+
}
|
|
25763
|
+
}
|
|
25764
|
+
}
|
|
25765
|
+
}
|
|
25766
|
+
}
|
|
25767
|
+
const affectedByImports = Array.from(affected);
|
|
25768
|
+
const allAffected = [...directlyChanged, ...affectedByImports];
|
|
25769
|
+
return {
|
|
25770
|
+
directlyChanged,
|
|
25771
|
+
affectedByImports,
|
|
25772
|
+
deleted: diff.deleted,
|
|
25773
|
+
allAffected
|
|
25774
|
+
};
|
|
25775
|
+
}
|
|
25776
|
+
function affectedDomains(changedFiles) {
|
|
25777
|
+
const domains = /* @__PURE__ */ new Set();
|
|
25778
|
+
for (const file of changedFiles) {
|
|
25779
|
+
const lower = file.toLowerCase();
|
|
25780
|
+
if (lower.endsWith(".tsx") || lower.endsWith(".jsx") || lower.endsWith(".css") || lower.endsWith(".scss") || lower.endsWith(".vue") || lower.endsWith(".svelte") || lower.includes("/components/") || lower.includes("/pages/") || lower.includes("/app/")) {
|
|
25781
|
+
domains.add("frontend");
|
|
25782
|
+
domains.add("uxui");
|
|
25783
|
+
}
|
|
25784
|
+
if (lower.includes(".test.") || lower.includes(".spec.") || lower.includes("__tests__") || lower.includes("/test/")) {
|
|
25785
|
+
domains.add("testing");
|
|
25786
|
+
}
|
|
25787
|
+
if (lower.includes("dockerfile") || lower.includes("docker-compose") || lower.includes(".dockerignore") || lower.includes(".github/") || lower.includes("ci/") || lower.includes("cd/")) {
|
|
25788
|
+
domains.add("devops");
|
|
25789
|
+
}
|
|
25790
|
+
if (lower.endsWith(".sql") || lower.includes("prisma") || lower.includes("drizzle") || lower.includes("migration") || lower.includes("/db/")) {
|
|
25791
|
+
domains.add("database");
|
|
25792
|
+
}
|
|
25793
|
+
if ((lower.endsWith(".ts") || lower.endsWith(".js")) && !lower.includes(".test.") && !lower.includes(".spec.") && !lower.endsWith(".d.ts")) {
|
|
25794
|
+
domains.add("backend");
|
|
25795
|
+
}
|
|
25796
|
+
}
|
|
25797
|
+
return domains;
|
|
25798
|
+
}
|
|
25799
|
+
var init_change_propagator = __esm({
|
|
25800
|
+
"core/domain/change-propagator.ts"() {
|
|
25801
|
+
"use strict";
|
|
25802
|
+
init_import_graph();
|
|
25803
|
+
__name(propagateChanges, "propagateChanges");
|
|
25804
|
+
__name(affectedDomains, "affectedDomains");
|
|
25805
|
+
}
|
|
25806
|
+
});
|
|
25807
|
+
|
|
25808
|
+
// core/domain/file-hasher.ts
|
|
25524
25809
|
import fs48 from "node:fs/promises";
|
|
25525
25810
|
import path51 from "node:path";
|
|
25811
|
+
async function listProjectFiles(dir, projectPath) {
|
|
25812
|
+
const files = [];
|
|
25813
|
+
const entries = await fs48.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
25814
|
+
for (const entry of entries) {
|
|
25815
|
+
const name = String(entry.name);
|
|
25816
|
+
if (SKIP_DIRS3.has(name)) continue;
|
|
25817
|
+
if (name.startsWith(".") && name !== ".env.example") continue;
|
|
25818
|
+
const fullPath = path51.join(dir, name);
|
|
25819
|
+
if (entry.isDirectory()) {
|
|
25820
|
+
files.push(...await listProjectFiles(fullPath, projectPath));
|
|
25821
|
+
} else if (entry.isFile()) {
|
|
25822
|
+
const ext = path51.extname(name).toLowerCase();
|
|
25823
|
+
if (INDEXABLE_EXTENSIONS3.has(ext)) {
|
|
25824
|
+
files.push(path51.relative(projectPath, fullPath));
|
|
25825
|
+
}
|
|
25826
|
+
}
|
|
25827
|
+
}
|
|
25828
|
+
return files;
|
|
25829
|
+
}
|
|
25830
|
+
function hashContent(content) {
|
|
25831
|
+
if (typeof Bun !== "undefined" && Bun.hash) {
|
|
25832
|
+
return `xxh64:${Bun.hash(content).toString(36)}`;
|
|
25833
|
+
}
|
|
25834
|
+
let h = 2166136261;
|
|
25835
|
+
for (let i = 0; i < content.length; i++) {
|
|
25836
|
+
h ^= content.charCodeAt(i);
|
|
25837
|
+
h = Math.imul(h, 16777619);
|
|
25838
|
+
}
|
|
25839
|
+
return `fnv1a:${(h >>> 0).toString(36)}`;
|
|
25840
|
+
}
|
|
25841
|
+
async function computeHashes(projectPath) {
|
|
25842
|
+
const filePaths = await listProjectFiles(projectPath, projectPath);
|
|
25843
|
+
const hashes = /* @__PURE__ */ new Map();
|
|
25844
|
+
const BATCH_SIZE = 100;
|
|
25845
|
+
for (let i = 0; i < filePaths.length; i += BATCH_SIZE) {
|
|
25846
|
+
const batch = filePaths.slice(i, i + BATCH_SIZE);
|
|
25847
|
+
const results = await Promise.all(
|
|
25848
|
+
batch.map(async (filePath) => {
|
|
25849
|
+
try {
|
|
25850
|
+
const fullPath = path51.join(projectPath, filePath);
|
|
25851
|
+
const [content, stat] = await Promise.all([
|
|
25852
|
+
fs48.readFile(fullPath, "utf-8"),
|
|
25853
|
+
fs48.stat(fullPath)
|
|
25854
|
+
]);
|
|
25855
|
+
return {
|
|
25856
|
+
path: filePath,
|
|
25857
|
+
hash: hashContent(content),
|
|
25858
|
+
size: stat.size,
|
|
25859
|
+
mtime: stat.mtime.toISOString()
|
|
25860
|
+
};
|
|
25861
|
+
} catch {
|
|
25862
|
+
return null;
|
|
25863
|
+
}
|
|
25864
|
+
})
|
|
25865
|
+
);
|
|
25866
|
+
for (const result of results) {
|
|
25867
|
+
if (result) {
|
|
25868
|
+
hashes.set(result.path, result);
|
|
25869
|
+
}
|
|
25870
|
+
}
|
|
25871
|
+
}
|
|
25872
|
+
return hashes;
|
|
25873
|
+
}
|
|
25874
|
+
function diffHashes(current, stored) {
|
|
25875
|
+
const added = [];
|
|
25876
|
+
const modified = [];
|
|
25877
|
+
const unchanged = [];
|
|
25878
|
+
for (const [filePath, currentHash] of current) {
|
|
25879
|
+
const storedHash = stored.get(filePath);
|
|
25880
|
+
if (!storedHash) {
|
|
25881
|
+
added.push(filePath);
|
|
25882
|
+
} else if (storedHash.hash !== currentHash.hash) {
|
|
25883
|
+
modified.push(filePath);
|
|
25884
|
+
} else {
|
|
25885
|
+
unchanged.push(filePath);
|
|
25886
|
+
}
|
|
25887
|
+
}
|
|
25888
|
+
const deleted = [];
|
|
25889
|
+
for (const filePath of stored.keys()) {
|
|
25890
|
+
if (!current.has(filePath)) {
|
|
25891
|
+
deleted.push(filePath);
|
|
25892
|
+
}
|
|
25893
|
+
}
|
|
25894
|
+
return { added, modified, deleted, unchanged };
|
|
25895
|
+
}
|
|
25896
|
+
function saveHashes(projectId, hashes) {
|
|
25897
|
+
const db = database_default.getDb(projectId);
|
|
25898
|
+
db.transaction(() => {
|
|
25899
|
+
db.prepare("DELETE FROM index_checksums").run();
|
|
25900
|
+
const insert = db.prepare(
|
|
25901
|
+
"INSERT INTO index_checksums (path, checksum, size, mtime) VALUES (?, ?, ?, ?)"
|
|
25902
|
+
);
|
|
25903
|
+
for (const [, hash] of hashes) {
|
|
25904
|
+
insert.run(hash.path, hash.hash, hash.size, hash.mtime);
|
|
25905
|
+
}
|
|
25906
|
+
})();
|
|
25907
|
+
database_default.setDoc(projectId, "file-hashes-meta", {
|
|
25908
|
+
fileCount: hashes.size,
|
|
25909
|
+
builtAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
25910
|
+
});
|
|
25911
|
+
}
|
|
25912
|
+
function loadHashes(projectId) {
|
|
25913
|
+
const hashes = /* @__PURE__ */ new Map();
|
|
25914
|
+
try {
|
|
25915
|
+
const rows = database_default.query(
|
|
25916
|
+
projectId,
|
|
25917
|
+
"SELECT path, checksum, size, mtime FROM index_checksums"
|
|
25918
|
+
);
|
|
25919
|
+
for (const row of rows) {
|
|
25920
|
+
hashes.set(row.path, {
|
|
25921
|
+
path: row.path,
|
|
25922
|
+
hash: row.checksum,
|
|
25923
|
+
size: row.size || 0,
|
|
25924
|
+
mtime: row.mtime || ""
|
|
25925
|
+
});
|
|
25926
|
+
}
|
|
25927
|
+
} catch {
|
|
25928
|
+
}
|
|
25929
|
+
return hashes;
|
|
25930
|
+
}
|
|
25931
|
+
async function detectChanges(projectPath, projectId) {
|
|
25932
|
+
const [currentHashes, storedHashes] = await Promise.all([
|
|
25933
|
+
computeHashes(projectPath),
|
|
25934
|
+
Promise.resolve(loadHashes(projectId))
|
|
25935
|
+
]);
|
|
25936
|
+
const diff = diffHashes(currentHashes, storedHashes);
|
|
25937
|
+
return { diff, currentHashes };
|
|
25938
|
+
}
|
|
25939
|
+
function hasHashRegistry(projectId) {
|
|
25940
|
+
return database_default.hasDoc(projectId, "file-hashes-meta");
|
|
25941
|
+
}
|
|
25942
|
+
var INDEXABLE_EXTENSIONS3, SKIP_DIRS3;
|
|
25943
|
+
var init_file_hasher = __esm({
|
|
25944
|
+
"core/domain/file-hasher.ts"() {
|
|
25945
|
+
"use strict";
|
|
25946
|
+
init_database();
|
|
25947
|
+
INDEXABLE_EXTENSIONS3 = /* @__PURE__ */ new Set([
|
|
25948
|
+
".ts",
|
|
25949
|
+
".tsx",
|
|
25950
|
+
".js",
|
|
25951
|
+
".jsx",
|
|
25952
|
+
".mjs",
|
|
25953
|
+
".cjs",
|
|
25954
|
+
".json",
|
|
25955
|
+
".md",
|
|
25956
|
+
".css",
|
|
25957
|
+
".scss",
|
|
25958
|
+
".html",
|
|
25959
|
+
".vue",
|
|
25960
|
+
".svelte",
|
|
25961
|
+
".py",
|
|
25962
|
+
".go",
|
|
25963
|
+
".rs",
|
|
25964
|
+
".yaml",
|
|
25965
|
+
".yml",
|
|
25966
|
+
".toml"
|
|
25967
|
+
]);
|
|
25968
|
+
SKIP_DIRS3 = /* @__PURE__ */ new Set([
|
|
25969
|
+
"node_modules",
|
|
25970
|
+
".git",
|
|
25971
|
+
"dist",
|
|
25972
|
+
"build",
|
|
25973
|
+
"out",
|
|
25974
|
+
".next",
|
|
25975
|
+
"coverage",
|
|
25976
|
+
".cache",
|
|
25977
|
+
".turbo",
|
|
25978
|
+
".vercel",
|
|
25979
|
+
".prjct"
|
|
25980
|
+
]);
|
|
25981
|
+
__name(listProjectFiles, "listProjectFiles");
|
|
25982
|
+
__name(hashContent, "hashContent");
|
|
25983
|
+
__name(computeHashes, "computeHashes");
|
|
25984
|
+
__name(diffHashes, "diffHashes");
|
|
25985
|
+
__name(saveHashes, "saveHashes");
|
|
25986
|
+
__name(loadHashes, "loadHashes");
|
|
25987
|
+
__name(detectChanges, "detectChanges");
|
|
25988
|
+
__name(hasHashRegistry, "hasHashRegistry");
|
|
25989
|
+
}
|
|
25990
|
+
});
|
|
25991
|
+
|
|
25992
|
+
// core/domain/git-cochange.ts
|
|
25993
|
+
import { exec as execCallback7 } from "node:child_process";
|
|
25994
|
+
import { promisify as promisify15 } from "node:util";
|
|
25995
|
+
async function parseGitLog(projectPath, maxCommits = 100) {
|
|
25996
|
+
try {
|
|
25997
|
+
const { stdout } = await exec14(
|
|
25998
|
+
`git log --name-only --pretty=format:'---COMMIT---' -${maxCommits}`,
|
|
25999
|
+
{ cwd: projectPath, maxBuffer: 10 * 1024 * 1024 }
|
|
26000
|
+
);
|
|
26001
|
+
const commits = [];
|
|
26002
|
+
let currentFiles = null;
|
|
26003
|
+
for (const line of stdout.split("\n")) {
|
|
26004
|
+
const trimmed = line.trim();
|
|
26005
|
+
if (trimmed === "---COMMIT---") {
|
|
26006
|
+
if (currentFiles && currentFiles.size > 0 && currentFiles.size <= MAX_FILES_PER_COMMIT) {
|
|
26007
|
+
commits.push(currentFiles);
|
|
26008
|
+
}
|
|
26009
|
+
currentFiles = /* @__PURE__ */ new Set();
|
|
26010
|
+
} else if (trimmed && currentFiles) {
|
|
26011
|
+
if (isSourceFile(trimmed)) {
|
|
26012
|
+
currentFiles.add(trimmed);
|
|
26013
|
+
}
|
|
26014
|
+
}
|
|
26015
|
+
}
|
|
26016
|
+
if (currentFiles && currentFiles.size > 0 && currentFiles.size <= MAX_FILES_PER_COMMIT) {
|
|
26017
|
+
commits.push(currentFiles);
|
|
26018
|
+
}
|
|
26019
|
+
return commits;
|
|
26020
|
+
} catch {
|
|
26021
|
+
return [];
|
|
26022
|
+
}
|
|
26023
|
+
}
|
|
26024
|
+
function isSourceFile(filePath) {
|
|
26025
|
+
const sourceExtensions = /\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|cs|rb|php|vue|svelte)$/i;
|
|
26026
|
+
return sourceExtensions.test(filePath) && !filePath.includes("node_modules/");
|
|
26027
|
+
}
|
|
26028
|
+
async function buildMatrix(projectPath, maxCommits = 100) {
|
|
26029
|
+
const commitSets = await parseGitLog(projectPath, maxCommits);
|
|
26030
|
+
const fileCommitCount = /* @__PURE__ */ new Map();
|
|
26031
|
+
const pairCount = /* @__PURE__ */ new Map();
|
|
26032
|
+
for (const files of commitSets) {
|
|
26033
|
+
const fileArray = Array.from(files);
|
|
26034
|
+
for (const file of fileArray) {
|
|
26035
|
+
fileCommitCount.set(file, (fileCommitCount.get(file) || 0) + 1);
|
|
26036
|
+
}
|
|
26037
|
+
for (let i = 0; i < fileArray.length; i++) {
|
|
26038
|
+
for (let j = i + 1; j < fileArray.length; j++) {
|
|
26039
|
+
const key = pairKey(fileArray[i], fileArray[j]);
|
|
26040
|
+
pairCount.set(key, (pairCount.get(key) || 0) + 1);
|
|
26041
|
+
}
|
|
26042
|
+
}
|
|
26043
|
+
}
|
|
26044
|
+
const matrix = {};
|
|
26045
|
+
for (const [key, count] of pairCount) {
|
|
26046
|
+
const [fileA, fileB] = key.split("\0");
|
|
26047
|
+
const countA = fileCommitCount.get(fileA) || 0;
|
|
26048
|
+
const countB = fileCommitCount.get(fileB) || 0;
|
|
26049
|
+
if (countA < MIN_FILE_OCCURRENCES || countB < MIN_FILE_OCCURRENCES) continue;
|
|
26050
|
+
const unionCount = countA + countB - count;
|
|
26051
|
+
const similarity = unionCount > 0 ? count / unionCount : 0;
|
|
26052
|
+
if (similarity < MIN_SIMILARITY) continue;
|
|
26053
|
+
if (!matrix[fileA]) matrix[fileA] = {};
|
|
26054
|
+
if (!matrix[fileB]) matrix[fileB] = {};
|
|
26055
|
+
matrix[fileA][fileB] = similarity;
|
|
26056
|
+
matrix[fileB][fileA] = similarity;
|
|
26057
|
+
}
|
|
26058
|
+
return {
|
|
26059
|
+
matrix,
|
|
26060
|
+
commitsAnalyzed: commitSets.length,
|
|
26061
|
+
filesAnalyzed: fileCommitCount.size,
|
|
26062
|
+
builtAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
26063
|
+
};
|
|
26064
|
+
}
|
|
26065
|
+
function pairKey(a, b) {
|
|
26066
|
+
return a < b ? `${a}\0${b}` : `${b}\0${a}`;
|
|
26067
|
+
}
|
|
26068
|
+
function saveMatrix(projectId, index) {
|
|
26069
|
+
database_default.setDoc(projectId, INDEX_KEY3, index);
|
|
26070
|
+
}
|
|
26071
|
+
async function indexCoChanges(projectPath, projectId, maxCommits = 100) {
|
|
26072
|
+
const index = await buildMatrix(projectPath, maxCommits);
|
|
26073
|
+
saveMatrix(projectId, index);
|
|
26074
|
+
return index;
|
|
26075
|
+
}
|
|
26076
|
+
var exec14, MIN_SIMILARITY, MIN_FILE_OCCURRENCES, MAX_FILES_PER_COMMIT, INDEX_KEY3;
|
|
26077
|
+
var init_git_cochange = __esm({
|
|
26078
|
+
"core/domain/git-cochange.ts"() {
|
|
26079
|
+
"use strict";
|
|
26080
|
+
init_database();
|
|
26081
|
+
exec14 = promisify15(execCallback7);
|
|
26082
|
+
MIN_SIMILARITY = 0.1;
|
|
26083
|
+
MIN_FILE_OCCURRENCES = 2;
|
|
26084
|
+
MAX_FILES_PER_COMMIT = 30;
|
|
26085
|
+
__name(parseGitLog, "parseGitLog");
|
|
26086
|
+
__name(isSourceFile, "isSourceFile");
|
|
26087
|
+
__name(buildMatrix, "buildMatrix");
|
|
26088
|
+
__name(pairKey, "pairKey");
|
|
26089
|
+
INDEX_KEY3 = "cochange-index";
|
|
26090
|
+
__name(saveMatrix, "saveMatrix");
|
|
26091
|
+
__name(indexCoChanges, "indexCoChanges");
|
|
26092
|
+
}
|
|
26093
|
+
});
|
|
26094
|
+
|
|
26095
|
+
// core/services/context-generator.ts
|
|
26096
|
+
import fs49 from "node:fs/promises";
|
|
26097
|
+
import path52 from "node:path";
|
|
25526
26098
|
var ContextFileGenerator;
|
|
25527
26099
|
var init_context_generator = __esm({
|
|
25528
26100
|
"core/services/context-generator.ts"() {
|
|
@@ -25547,10 +26119,10 @@ var init_context_generator = __esm({
|
|
|
25547
26119
|
async writeWithPreservation(filePath, content) {
|
|
25548
26120
|
let finalContent = content;
|
|
25549
26121
|
try {
|
|
25550
|
-
const existingContent = await
|
|
26122
|
+
const existingContent = await fs49.readFile(filePath, "utf-8");
|
|
25551
26123
|
const validation = validatePreserveBlocks(existingContent);
|
|
25552
26124
|
if (!validation.valid) {
|
|
25553
|
-
const filename =
|
|
26125
|
+
const filename = path52.basename(filePath);
|
|
25554
26126
|
console.warn(`\u26A0\uFE0F ${filename} has invalid preserve blocks:`);
|
|
25555
26127
|
for (const error of validation.errors) {
|
|
25556
26128
|
console.warn(` ${error}`);
|
|
@@ -25559,13 +26131,13 @@ var init_context_generator = __esm({
|
|
|
25559
26131
|
finalContent = mergePreservedSections(content, existingContent);
|
|
25560
26132
|
} catch {
|
|
25561
26133
|
}
|
|
25562
|
-
await
|
|
26134
|
+
await fs49.writeFile(filePath, finalContent, "utf-8");
|
|
25563
26135
|
}
|
|
25564
26136
|
/**
|
|
25565
26137
|
* Generate all context files in parallel
|
|
25566
26138
|
*/
|
|
25567
26139
|
async generate(git, stats, commands, agents, sources) {
|
|
25568
|
-
const contextPath =
|
|
26140
|
+
const contextPath = path52.join(this.config.globalPath, "context");
|
|
25569
26141
|
await Promise.all([
|
|
25570
26142
|
this.generateClaudeMd(contextPath, git, stats, commands, agents, sources),
|
|
25571
26143
|
this.generateNowMd(contextPath),
|
|
@@ -25665,7 +26237,7 @@ Load from \`~/.prjct-cli/projects/${this.config.projectId}/agents/\`:
|
|
|
25665
26237
|
**Workflow**: ${workflowAgents.join(", ")}
|
|
25666
26238
|
**Domain**: ${domainAgents.join(", ") || "none"}
|
|
25667
26239
|
`;
|
|
25668
|
-
const claudePath =
|
|
26240
|
+
const claudePath = path52.join(contextPath, "CLAUDE.md");
|
|
25669
26241
|
await this.writeWithPreservation(claudePath, content);
|
|
25670
26242
|
}
|
|
25671
26243
|
/**
|
|
@@ -25674,8 +26246,8 @@ Load from \`~/.prjct-cli/projects/${this.config.projectId}/agents/\`:
|
|
|
25674
26246
|
async generateNowMd(contextPath) {
|
|
25675
26247
|
let currentTask = null;
|
|
25676
26248
|
try {
|
|
25677
|
-
const statePath =
|
|
25678
|
-
const state = JSON.parse(await
|
|
26249
|
+
const statePath = path52.join(this.config.globalPath, "storage", "state.json");
|
|
26250
|
+
const state = JSON.parse(await fs49.readFile(statePath, "utf-8"));
|
|
25679
26251
|
currentTask = state.currentTask;
|
|
25680
26252
|
} catch {
|
|
25681
26253
|
}
|
|
@@ -25691,7 +26263,7 @@ _No active task_
|
|
|
25691
26263
|
|
|
25692
26264
|
Use \`p. task "description"\` to start working.
|
|
25693
26265
|
`;
|
|
25694
|
-
await this.writeWithPreservation(
|
|
26266
|
+
await this.writeWithPreservation(path52.join(contextPath, "now.md"), content);
|
|
25695
26267
|
}
|
|
25696
26268
|
/**
|
|
25697
26269
|
* Generate next.md - task queue
|
|
@@ -25699,15 +26271,15 @@ Use \`p. task "description"\` to start working.
|
|
|
25699
26271
|
async generateNextMd(contextPath) {
|
|
25700
26272
|
let queue = { tasks: [] };
|
|
25701
26273
|
try {
|
|
25702
|
-
const queuePath =
|
|
25703
|
-
queue = JSON.parse(await
|
|
26274
|
+
const queuePath = path52.join(this.config.globalPath, "storage", "queue.json");
|
|
26275
|
+
queue = JSON.parse(await fs49.readFile(queuePath, "utf-8"));
|
|
25704
26276
|
} catch {
|
|
25705
26277
|
}
|
|
25706
26278
|
const content = `# NEXT
|
|
25707
26279
|
|
|
25708
26280
|
${queue.tasks.length > 0 ? queue.tasks.map((t, i) => `${i + 1}. ${t.description}${t.priority ? ` [${t.priority}]` : ""}`).join("\n") : "_Empty queue_"}
|
|
25709
26281
|
`;
|
|
25710
|
-
await this.writeWithPreservation(
|
|
26282
|
+
await this.writeWithPreservation(path52.join(contextPath, "next.md"), content);
|
|
25711
26283
|
}
|
|
25712
26284
|
/**
|
|
25713
26285
|
* Generate ideas.md - captured ideas
|
|
@@ -25715,15 +26287,15 @@ ${queue.tasks.length > 0 ? queue.tasks.map((t, i) => `${i + 1}. ${t.description}
|
|
|
25715
26287
|
async generateIdeasMd(contextPath) {
|
|
25716
26288
|
let ideas = { ideas: [] };
|
|
25717
26289
|
try {
|
|
25718
|
-
const ideasPath =
|
|
25719
|
-
ideas = JSON.parse(await
|
|
26290
|
+
const ideasPath = path52.join(this.config.globalPath, "storage", "ideas.json");
|
|
26291
|
+
ideas = JSON.parse(await fs49.readFile(ideasPath, "utf-8"));
|
|
25720
26292
|
} catch {
|
|
25721
26293
|
}
|
|
25722
26294
|
const content = `# IDEAS
|
|
25723
26295
|
|
|
25724
26296
|
${ideas.ideas.length > 0 ? ideas.ideas.map((i) => `- ${i.text}${i.priority ? ` [${i.priority}]` : ""}`).join("\n") : "_No ideas captured yet_"}
|
|
25725
26297
|
`;
|
|
25726
|
-
await this.writeWithPreservation(
|
|
26298
|
+
await this.writeWithPreservation(path52.join(contextPath, "ideas.md"), content);
|
|
25727
26299
|
}
|
|
25728
26300
|
/**
|
|
25729
26301
|
* Generate shipped.md - completed features
|
|
@@ -25733,8 +26305,8 @@ ${ideas.ideas.length > 0 ? ideas.ideas.map((i) => `- ${i.text}${i.priority ? ` [
|
|
|
25733
26305
|
shipped: []
|
|
25734
26306
|
};
|
|
25735
26307
|
try {
|
|
25736
|
-
const shippedPath =
|
|
25737
|
-
shipped = JSON.parse(await
|
|
26308
|
+
const shippedPath = path52.join(this.config.globalPath, "storage", "shipped.json");
|
|
26309
|
+
shipped = JSON.parse(await fs49.readFile(shippedPath, "utf-8"));
|
|
25738
26310
|
} catch {
|
|
25739
26311
|
}
|
|
25740
26312
|
const content = `# SHIPPED \u{1F680}
|
|
@@ -25743,7 +26315,7 @@ ${shipped.shipped.length > 0 ? shipped.shipped.slice(-10).map((s) => `- **${s.na
|
|
|
25743
26315
|
|
|
25744
26316
|
**Total shipped:** ${shipped.shipped.length}
|
|
25745
26317
|
`;
|
|
25746
|
-
await this.writeWithPreservation(
|
|
26318
|
+
await this.writeWithPreservation(path52.join(contextPath, "shipped.md"), content);
|
|
25747
26319
|
}
|
|
25748
26320
|
// ==========================================================================
|
|
25749
26321
|
// MONOREPO SUPPORT
|
|
@@ -25772,9 +26344,9 @@ ${shipped.shipped.length > 0 ? shipped.shipped.slice(-10).map((s) => `- **${s.na
|
|
|
25772
26344
|
commands,
|
|
25773
26345
|
agents
|
|
25774
26346
|
);
|
|
25775
|
-
const claudePath =
|
|
26347
|
+
const claudePath = path52.join(pkg.path, "CLAUDE.md");
|
|
25776
26348
|
await this.writeWithPreservation(claudePath, content);
|
|
25777
|
-
generatedFiles.push(
|
|
26349
|
+
generatedFiles.push(path52.relative(this.config.projectPath, claudePath));
|
|
25778
26350
|
}
|
|
25779
26351
|
return generatedFiles;
|
|
25780
26352
|
}
|
|
@@ -25787,8 +26359,8 @@ ${shipped.shipped.length > 0 ? shipped.shipped.slice(-10).map((s) => `- **${s.na
|
|
|
25787
26359
|
let pkgVersion = stats.version;
|
|
25788
26360
|
let pkgName = pkg.name;
|
|
25789
26361
|
try {
|
|
25790
|
-
const pkgJsonPath =
|
|
25791
|
-
const pkgJson = JSON.parse(await
|
|
26362
|
+
const pkgJsonPath = path52.join(pkg.path, "package.json");
|
|
26363
|
+
const pkgJson = JSON.parse(await fs49.readFile(pkgJsonPath, "utf-8"));
|
|
25792
26364
|
pkgVersion = pkgJson.version || stats.version;
|
|
25793
26365
|
pkgName = pkgJson.name || pkg.name;
|
|
25794
26366
|
} catch {
|
|
@@ -25853,8 +26425,8 @@ Load from \`~/.prjct-cli/projects/${this.config.projectId}/agents/\`:
|
|
|
25853
26425
|
});
|
|
25854
26426
|
|
|
25855
26427
|
// core/services/local-state-generator.ts
|
|
25856
|
-
import
|
|
25857
|
-
import
|
|
26428
|
+
import fs50 from "node:fs/promises";
|
|
26429
|
+
import path53 from "node:path";
|
|
25858
26430
|
var LOCAL_STATE_FILENAME, LocalStateGenerator, localStateGenerator;
|
|
25859
26431
|
var init_local_state_generator = __esm({
|
|
25860
26432
|
"core/services/local-state-generator.ts"() {
|
|
@@ -25869,17 +26441,17 @@ var init_local_state_generator = __esm({
|
|
|
25869
26441
|
* Generate .prjct-state.md in the project root
|
|
25870
26442
|
*/
|
|
25871
26443
|
async generate(projectPath, state) {
|
|
25872
|
-
const filePath =
|
|
26444
|
+
const filePath = path53.join(projectPath, LOCAL_STATE_FILENAME);
|
|
25873
26445
|
const content = this.toMarkdown(state);
|
|
25874
|
-
await
|
|
26446
|
+
await fs50.writeFile(filePath, content, "utf-8");
|
|
25875
26447
|
}
|
|
25876
26448
|
/**
|
|
25877
26449
|
* Remove local state file
|
|
25878
26450
|
*/
|
|
25879
26451
|
async remove(projectPath) {
|
|
25880
|
-
const filePath =
|
|
26452
|
+
const filePath = path53.join(projectPath, LOCAL_STATE_FILENAME);
|
|
25881
26453
|
try {
|
|
25882
|
-
await
|
|
26454
|
+
await fs50.unlink(filePath);
|
|
25883
26455
|
} catch (error) {
|
|
25884
26456
|
if (!isNotFoundError(error)) throw error;
|
|
25885
26457
|
}
|
|
@@ -25888,9 +26460,9 @@ var init_local_state_generator = __esm({
|
|
|
25888
26460
|
* Check if local state file exists
|
|
25889
26461
|
*/
|
|
25890
26462
|
async exists(projectPath) {
|
|
25891
|
-
const filePath =
|
|
26463
|
+
const filePath = path53.join(projectPath, LOCAL_STATE_FILENAME);
|
|
25892
26464
|
try {
|
|
25893
|
-
await
|
|
26465
|
+
await fs50.access(filePath);
|
|
25894
26466
|
return true;
|
|
25895
26467
|
} catch {
|
|
25896
26468
|
return false;
|
|
@@ -25969,11 +26541,11 @@ var init_local_state_generator = __esm({
|
|
|
25969
26541
|
});
|
|
25970
26542
|
|
|
25971
26543
|
// core/services/skill-lock.ts
|
|
25972
|
-
import
|
|
26544
|
+
import fs51 from "node:fs/promises";
|
|
25973
26545
|
import os14 from "node:os";
|
|
25974
|
-
import
|
|
26546
|
+
import path54 from "node:path";
|
|
25975
26547
|
function getLockFilePath() {
|
|
25976
|
-
return
|
|
26548
|
+
return path54.join(os14.homedir(), ".prjct-cli", "skills", LOCK_FILE_NAME);
|
|
25977
26549
|
}
|
|
25978
26550
|
function createEmptyLockFile() {
|
|
25979
26551
|
return {
|
|
@@ -25984,7 +26556,7 @@ function createEmptyLockFile() {
|
|
|
25984
26556
|
}
|
|
25985
26557
|
async function read() {
|
|
25986
26558
|
try {
|
|
25987
|
-
const content = await
|
|
26559
|
+
const content = await fs51.readFile(getLockFilePath(), "utf-8");
|
|
25988
26560
|
return JSON.parse(content);
|
|
25989
26561
|
} catch {
|
|
25990
26562
|
return createEmptyLockFile();
|
|
@@ -25992,9 +26564,9 @@ async function read() {
|
|
|
25992
26564
|
}
|
|
25993
26565
|
async function write(lockFile) {
|
|
25994
26566
|
const lockPath = getLockFilePath();
|
|
25995
|
-
await
|
|
26567
|
+
await fs51.mkdir(path54.dirname(lockPath), { recursive: true });
|
|
25996
26568
|
lockFile.generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
25997
|
-
await
|
|
26569
|
+
await fs51.writeFile(lockPath, JSON.stringify(lockFile, null, 2), "utf-8");
|
|
25998
26570
|
}
|
|
25999
26571
|
async function addEntry(entry) {
|
|
26000
26572
|
const lockFile = await read();
|
|
@@ -26047,14 +26619,14 @@ var init_skill_lock = __esm({
|
|
|
26047
26619
|
|
|
26048
26620
|
// core/services/skill-installer.ts
|
|
26049
26621
|
import { exec as execCallback8 } from "node:child_process";
|
|
26050
|
-
import
|
|
26622
|
+
import fs52 from "node:fs/promises";
|
|
26051
26623
|
import os15 from "node:os";
|
|
26052
|
-
import
|
|
26624
|
+
import path55 from "node:path";
|
|
26053
26625
|
import { promisify as promisify16 } from "node:util";
|
|
26054
26626
|
import { glob } from "glob";
|
|
26055
26627
|
function parseSource(source) {
|
|
26056
26628
|
if (source.startsWith("./") || source.startsWith("/") || source.startsWith("~")) {
|
|
26057
|
-
const resolvedPath = source.startsWith("~") ?
|
|
26629
|
+
const resolvedPath = source.startsWith("~") ? path55.join(os15.homedir(), source.slice(1)) : path55.resolve(source);
|
|
26058
26630
|
return {
|
|
26059
26631
|
type: "local",
|
|
26060
26632
|
localPath: resolvedPath,
|
|
@@ -26092,22 +26664,22 @@ function parseSource(source) {
|
|
|
26092
26664
|
async function discoverSkills(dir) {
|
|
26093
26665
|
const skills = [];
|
|
26094
26666
|
try {
|
|
26095
|
-
const rootSkill =
|
|
26096
|
-
await
|
|
26097
|
-
const dirName =
|
|
26667
|
+
const rootSkill = path55.join(dir, "SKILL.md");
|
|
26668
|
+
await fs52.access(rootSkill);
|
|
26669
|
+
const dirName = path55.basename(dir);
|
|
26098
26670
|
skills.push({ name: dirName, filePath: rootSkill });
|
|
26099
26671
|
} catch {
|
|
26100
26672
|
}
|
|
26101
26673
|
const subdirSkills = await glob("*/SKILL.md", { cwd: dir, absolute: true });
|
|
26102
26674
|
for (const filePath of subdirSkills) {
|
|
26103
|
-
const name =
|
|
26675
|
+
const name = path55.basename(path55.dirname(filePath));
|
|
26104
26676
|
if (!skills.some((s) => s.name === name)) {
|
|
26105
26677
|
skills.push({ name, filePath });
|
|
26106
26678
|
}
|
|
26107
26679
|
}
|
|
26108
26680
|
const nestedSkills = await glob("skills/*/SKILL.md", { cwd: dir, absolute: true });
|
|
26109
26681
|
for (const filePath of nestedSkills) {
|
|
26110
|
-
const name =
|
|
26682
|
+
const name = path55.basename(path55.dirname(filePath));
|
|
26111
26683
|
if (!skills.some((s) => s.name === name)) {
|
|
26112
26684
|
skills.push({ name, filePath });
|
|
26113
26685
|
}
|
|
@@ -26143,16 +26715,16 @@ ${prjctBlock.join("\n")}
|
|
|
26143
26715
|
${content}`;
|
|
26144
26716
|
}
|
|
26145
26717
|
function getInstallDir() {
|
|
26146
|
-
return
|
|
26718
|
+
return path55.join(os15.homedir(), ".claude", "skills");
|
|
26147
26719
|
}
|
|
26148
26720
|
async function installSkillFile(sourcePath, name, source, sha) {
|
|
26149
26721
|
const installDir = getInstallDir();
|
|
26150
|
-
const targetDir =
|
|
26151
|
-
const targetPath =
|
|
26152
|
-
const content = await
|
|
26722
|
+
const targetDir = path55.join(installDir, name);
|
|
26723
|
+
const targetPath = path55.join(targetDir, "SKILL.md");
|
|
26724
|
+
const content = await fs52.readFile(sourcePath, "utf-8");
|
|
26153
26725
|
const enrichedContent = injectSourceMetadata(content, source, sha);
|
|
26154
|
-
await
|
|
26155
|
-
await
|
|
26726
|
+
await fs52.mkdir(targetDir, { recursive: true });
|
|
26727
|
+
await fs52.writeFile(targetPath, enrichedContent, "utf-8");
|
|
26156
26728
|
return {
|
|
26157
26729
|
name,
|
|
26158
26730
|
filePath: targetPath,
|
|
@@ -26169,7 +26741,7 @@ async function installFromGitHub(source) {
|
|
|
26169
26741
|
);
|
|
26170
26742
|
return result;
|
|
26171
26743
|
}
|
|
26172
|
-
const tmpDir =
|
|
26744
|
+
const tmpDir = path55.join(os15.tmpdir(), `prjct-skill-${Date.now()}`);
|
|
26173
26745
|
try {
|
|
26174
26746
|
const cloneUrl = `https://github.com/${source.owner}/${source.repo}.git`;
|
|
26175
26747
|
await exec15(`git clone --depth 1 ${cloneUrl} ${tmpDir}`, { timeout: getTimeout("GIT_CLONE") });
|
|
@@ -26213,7 +26785,7 @@ async function installFromGitHub(source) {
|
|
|
26213
26785
|
}
|
|
26214
26786
|
} finally {
|
|
26215
26787
|
try {
|
|
26216
|
-
await
|
|
26788
|
+
await fs52.rm(tmpDir, { recursive: true, force: true });
|
|
26217
26789
|
} catch {
|
|
26218
26790
|
}
|
|
26219
26791
|
}
|
|
@@ -26223,14 +26795,14 @@ async function installFromLocal(source) {
|
|
|
26223
26795
|
const result = { installed: [], skipped: [], errors: [] };
|
|
26224
26796
|
const localPath = source.localPath;
|
|
26225
26797
|
try {
|
|
26226
|
-
await
|
|
26798
|
+
await fs52.access(localPath);
|
|
26227
26799
|
} catch {
|
|
26228
26800
|
result.errors.push(`Local path not found: ${localPath}`);
|
|
26229
26801
|
return result;
|
|
26230
26802
|
}
|
|
26231
|
-
const stat = await
|
|
26803
|
+
const stat = await fs52.stat(localPath);
|
|
26232
26804
|
if (stat.isFile()) {
|
|
26233
|
-
const name =
|
|
26805
|
+
const name = path55.basename(path55.dirname(localPath));
|
|
26234
26806
|
try {
|
|
26235
26807
|
const installed = await installSkillFile(localPath, name, source);
|
|
26236
26808
|
const lockEntry = {
|
|
@@ -26270,14 +26842,14 @@ async function installFromLocal(source) {
|
|
|
26270
26842
|
}
|
|
26271
26843
|
async function remove(name) {
|
|
26272
26844
|
const installDir = getInstallDir();
|
|
26273
|
-
const subdirPath =
|
|
26845
|
+
const subdirPath = path55.join(installDir, name);
|
|
26274
26846
|
try {
|
|
26275
|
-
await
|
|
26847
|
+
await fs52.rm(subdirPath, { recursive: true, force: true });
|
|
26276
26848
|
} catch {
|
|
26277
26849
|
}
|
|
26278
|
-
const flatPath =
|
|
26850
|
+
const flatPath = path55.join(installDir, `${name}.md`);
|
|
26279
26851
|
try {
|
|
26280
|
-
await
|
|
26852
|
+
await fs52.rm(flatPath, { force: true });
|
|
26281
26853
|
} catch {
|
|
26282
26854
|
}
|
|
26283
26855
|
return skillLock.removeEntry(name);
|
|
@@ -26325,8 +26897,8 @@ var init_skill_installer = __esm({
|
|
|
26325
26897
|
});
|
|
26326
26898
|
|
|
26327
26899
|
// core/services/stack-detector.ts
|
|
26328
|
-
import
|
|
26329
|
-
import
|
|
26900
|
+
import fs53 from "node:fs/promises";
|
|
26901
|
+
import path56 from "node:path";
|
|
26330
26902
|
var StackDetector;
|
|
26331
26903
|
var init_stack_detector = __esm({
|
|
26332
26904
|
"core/services/stack-detector.ts"() {
|
|
@@ -26485,8 +27057,8 @@ var init_stack_detector = __esm({
|
|
|
26485
27057
|
*/
|
|
26486
27058
|
async readPackageJson() {
|
|
26487
27059
|
try {
|
|
26488
|
-
const pkgPath =
|
|
26489
|
-
const content = await
|
|
27060
|
+
const pkgPath = path56.join(this.projectPath, "package.json");
|
|
27061
|
+
const content = await fs53.readFile(pkgPath, "utf-8");
|
|
26490
27062
|
return JSON.parse(content);
|
|
26491
27063
|
} catch {
|
|
26492
27064
|
return null;
|
|
@@ -26497,7 +27069,7 @@ var init_stack_detector = __esm({
|
|
|
26497
27069
|
*/
|
|
26498
27070
|
async fileExists(filename) {
|
|
26499
27071
|
try {
|
|
26500
|
-
await
|
|
27072
|
+
await fs53.access(path56.join(this.projectPath, filename));
|
|
26501
27073
|
return true;
|
|
26502
27074
|
} catch {
|
|
26503
27075
|
return false;
|
|
@@ -26509,8 +27081,8 @@ var init_stack_detector = __esm({
|
|
|
26509
27081
|
|
|
26510
27082
|
// core/services/sync-verifier.ts
|
|
26511
27083
|
import { exec as exec16 } from "node:child_process";
|
|
26512
|
-
import
|
|
26513
|
-
import
|
|
27084
|
+
import fs54 from "node:fs/promises";
|
|
27085
|
+
import path57 from "node:path";
|
|
26514
27086
|
import { promisify as promisify17 } from "node:util";
|
|
26515
27087
|
var execAsync10, BUILTIN_CHECKS, SyncVerifier, syncVerifier;
|
|
26516
27088
|
var init_sync_verifier = __esm({
|
|
@@ -26527,9 +27099,9 @@ var init_sync_verifier = __esm({
|
|
|
26527
27099
|
const expected = ["context/CLAUDE.md"];
|
|
26528
27100
|
const missing = [];
|
|
26529
27101
|
for (const file of expected) {
|
|
26530
|
-
const filePath =
|
|
27102
|
+
const filePath = path57.join(globalPath, file);
|
|
26531
27103
|
try {
|
|
26532
|
-
await
|
|
27104
|
+
await fs54.access(filePath);
|
|
26533
27105
|
} catch {
|
|
26534
27106
|
missing.push(file);
|
|
26535
27107
|
}
|
|
@@ -26550,9 +27122,9 @@ var init_sync_verifier = __esm({
|
|
|
26550
27122
|
const jsonFiles = ["storage/state.json"];
|
|
26551
27123
|
const invalid = [];
|
|
26552
27124
|
for (const file of jsonFiles) {
|
|
26553
|
-
const filePath =
|
|
27125
|
+
const filePath = path57.join(globalPath, file);
|
|
26554
27126
|
try {
|
|
26555
|
-
const content = await
|
|
27127
|
+
const content = await fs54.readFile(filePath, "utf-8");
|
|
26556
27128
|
JSON.parse(content);
|
|
26557
27129
|
} catch (error) {
|
|
26558
27130
|
if (!isNotFoundError(error)) {
|
|
@@ -26573,7 +27145,7 @@ var init_sync_verifier = __esm({
|
|
|
26573
27145
|
*/
|
|
26574
27146
|
async noSensitiveData(globalPath) {
|
|
26575
27147
|
const start = Date.now();
|
|
26576
|
-
const contextDir =
|
|
27148
|
+
const contextDir = path57.join(globalPath, "context");
|
|
26577
27149
|
const patterns = [
|
|
26578
27150
|
/(?:api[_-]?key|apikey)\s*[:=]\s*['"][^'"]{10,}/i,
|
|
26579
27151
|
/(?:password|passwd|pwd)\s*[:=]\s*['"][^'"]{4,}/i,
|
|
@@ -26581,10 +27153,10 @@ var init_sync_verifier = __esm({
|
|
|
26581
27153
|
];
|
|
26582
27154
|
const violations = [];
|
|
26583
27155
|
try {
|
|
26584
|
-
const files = await
|
|
27156
|
+
const files = await fs54.readdir(contextDir);
|
|
26585
27157
|
for (const file of files) {
|
|
26586
27158
|
if (!file.endsWith(".md")) continue;
|
|
26587
|
-
const content = await
|
|
27159
|
+
const content = await fs54.readFile(path57.join(contextDir, file), "utf-8");
|
|
26588
27160
|
for (const pattern of patterns) {
|
|
26589
27161
|
if (pattern.test(content)) {
|
|
26590
27162
|
violations.push(`${file}: potential sensitive data detected`);
|
|
@@ -26705,9 +27277,9 @@ var init_sync_verifier = __esm({
|
|
|
26705
27277
|
|
|
26706
27278
|
// core/services/sync-service.ts
|
|
26707
27279
|
import { exec as exec17 } from "node:child_process";
|
|
26708
|
-
import
|
|
27280
|
+
import fs55 from "node:fs/promises";
|
|
26709
27281
|
import os16 from "node:os";
|
|
26710
|
-
import
|
|
27282
|
+
import path58 from "node:path";
|
|
26711
27283
|
import { promisify as promisify18 } from "node:util";
|
|
26712
27284
|
var execAsync11, SyncService, syncService;
|
|
26713
27285
|
var init_sync_service = __esm({
|
|
@@ -26715,6 +27287,8 @@ var init_sync_service = __esm({
|
|
|
26715
27287
|
"use strict";
|
|
26716
27288
|
init_ai_tools();
|
|
26717
27289
|
init_bm25();
|
|
27290
|
+
init_change_propagator();
|
|
27291
|
+
init_file_hasher();
|
|
26718
27292
|
init_git_cochange();
|
|
26719
27293
|
init_import_graph();
|
|
26720
27294
|
init_errors();
|
|
@@ -26722,13 +27296,19 @@ var init_sync_service = __esm({
|
|
|
26722
27296
|
init_config_manager();
|
|
26723
27297
|
init_path_manager();
|
|
26724
27298
|
init_analysis_storage();
|
|
27299
|
+
init_archive_storage();
|
|
27300
|
+
init_ideas_storage();
|
|
26725
27301
|
init_metrics_storage();
|
|
26726
27302
|
init_migrate_json();
|
|
27303
|
+
init_queue_storage();
|
|
27304
|
+
init_shipped_storage();
|
|
27305
|
+
init_state_storage();
|
|
26727
27306
|
init_citations();
|
|
26728
27307
|
init_date_helper();
|
|
26729
27308
|
init_logger();
|
|
26730
27309
|
init_context_generator();
|
|
26731
27310
|
init_local_state_generator();
|
|
27311
|
+
init_memory_service();
|
|
26732
27312
|
init_skill_installer();
|
|
26733
27313
|
init_stack_detector();
|
|
26734
27314
|
init_sync_verifier();
|
|
@@ -26794,20 +27374,80 @@ var init_sync_service = __esm({
|
|
|
26794
27374
|
this.detectCommands(),
|
|
26795
27375
|
this.detectStack()
|
|
26796
27376
|
]);
|
|
26797
|
-
|
|
26798
|
-
|
|
26799
|
-
|
|
26800
|
-
|
|
26801
|
-
|
|
26802
|
-
|
|
26803
|
-
|
|
26804
|
-
|
|
26805
|
-
|
|
26806
|
-
|
|
27377
|
+
const isFullSync = options.full === true;
|
|
27378
|
+
let incrementalInfo;
|
|
27379
|
+
let shouldRebuildIndexes = true;
|
|
27380
|
+
let shouldRegenerateAgents = true;
|
|
27381
|
+
let changedDomains = /* @__PURE__ */ new Set();
|
|
27382
|
+
if (!isFullSync && hasHashRegistry(this.projectId)) {
|
|
27383
|
+
try {
|
|
27384
|
+
const { diff, currentHashes } = await detectChanges(this.projectPath, this.projectId);
|
|
27385
|
+
const totalChanged = diff.added.length + diff.modified.length + diff.deleted.length;
|
|
27386
|
+
if (totalChanged === 0 && !options.changedFiles?.length) {
|
|
27387
|
+
shouldRebuildIndexes = false;
|
|
27388
|
+
shouldRegenerateAgents = false;
|
|
27389
|
+
incrementalInfo = {
|
|
27390
|
+
isIncremental: true,
|
|
27391
|
+
filesChanged: 0,
|
|
27392
|
+
filesUnchanged: diff.unchanged.length,
|
|
27393
|
+
indexesRebuilt: false,
|
|
27394
|
+
agentsRegenerated: false,
|
|
27395
|
+
affectedDomains: []
|
|
27396
|
+
};
|
|
27397
|
+
} else {
|
|
27398
|
+
const propagated = propagateChanges(diff, this.projectId);
|
|
27399
|
+
changedDomains = affectedDomains(propagated.allAffected);
|
|
27400
|
+
const sourceExtensions = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
27401
|
+
const hasSourceChanges = propagated.allAffected.some((f) => {
|
|
27402
|
+
const ext = f.substring(f.lastIndexOf("."));
|
|
27403
|
+
return sourceExtensions.has(ext);
|
|
27404
|
+
});
|
|
27405
|
+
shouldRebuildIndexes = hasSourceChanges;
|
|
27406
|
+
const configChanged = propagated.directlyChanged.some(
|
|
27407
|
+
(f) => f === "package.json" || f === "tsconfig.json" || f.includes("Dockerfile") || f.includes("docker-compose")
|
|
27408
|
+
);
|
|
27409
|
+
shouldRegenerateAgents = configChanged;
|
|
27410
|
+
incrementalInfo = {
|
|
27411
|
+
isIncremental: true,
|
|
27412
|
+
filesChanged: totalChanged,
|
|
27413
|
+
filesUnchanged: diff.unchanged.length,
|
|
27414
|
+
indexesRebuilt: shouldRebuildIndexes,
|
|
27415
|
+
agentsRegenerated: shouldRegenerateAgents,
|
|
27416
|
+
affectedDomains: Array.from(changedDomains)
|
|
27417
|
+
};
|
|
27418
|
+
}
|
|
27419
|
+
saveHashes(this.projectId, currentHashes);
|
|
27420
|
+
} catch (error) {
|
|
27421
|
+
logger_default.debug("Incremental detection failed, falling back to full sync", {
|
|
27422
|
+
error: getErrorMessage(error)
|
|
27423
|
+
});
|
|
27424
|
+
}
|
|
27425
|
+
} else {
|
|
27426
|
+
try {
|
|
27427
|
+
const { currentHashes } = await detectChanges(this.projectPath, this.projectId);
|
|
27428
|
+
saveHashes(this.projectId, currentHashes);
|
|
27429
|
+
} catch (error) {
|
|
27430
|
+
logger_default.debug("Hash computation failed (non-critical)", {
|
|
27431
|
+
error: getErrorMessage(error)
|
|
27432
|
+
});
|
|
27433
|
+
}
|
|
26807
27434
|
}
|
|
26808
|
-
|
|
27435
|
+
if (shouldRebuildIndexes) {
|
|
27436
|
+
try {
|
|
27437
|
+
await Promise.all([
|
|
27438
|
+
indexProject(this.projectPath, this.projectId),
|
|
27439
|
+
indexImports(this.projectPath, this.projectId),
|
|
27440
|
+
indexCoChanges(this.projectPath, this.projectId)
|
|
27441
|
+
]);
|
|
27442
|
+
} catch (error) {
|
|
27443
|
+
logger_default.debug("File ranking index build failed (non-critical)", {
|
|
27444
|
+
error: getErrorMessage(error)
|
|
27445
|
+
});
|
|
27446
|
+
}
|
|
27447
|
+
}
|
|
27448
|
+
const agents = shouldRegenerateAgents ? await this.generateAgents(stack, stats) : await this.loadExistingAgents();
|
|
26809
27449
|
const skills = this.configureSkills(agents);
|
|
26810
|
-
const skillsInstalled = await this.autoInstallSkills(agents);
|
|
27450
|
+
const skillsInstalled = shouldRegenerateAgents ? await this.autoInstallSkills(agents) : [];
|
|
26811
27451
|
const sources = this.buildSources(stats, commands);
|
|
26812
27452
|
const contextFiles = await this.generateContextFiles(git, stats, commands, agents, sources);
|
|
26813
27453
|
const projectContext = {
|
|
@@ -26844,6 +27484,7 @@ var init_sync_service = __esm({
|
|
|
26844
27484
|
]);
|
|
26845
27485
|
const duration = Date.now() - startTime;
|
|
26846
27486
|
const syncMetrics = await this.recordSyncMetrics(stats, contextFiles, agents, duration);
|
|
27487
|
+
await this.archiveStaleData();
|
|
26847
27488
|
await command_installer_default.installGlobalConfig();
|
|
26848
27489
|
await command_installer_default.syncCommands();
|
|
26849
27490
|
let verification;
|
|
@@ -26875,7 +27516,8 @@ var init_sync_service = __esm({
|
|
|
26875
27516
|
success: r.success
|
|
26876
27517
|
})),
|
|
26877
27518
|
syncMetrics,
|
|
26878
|
-
verification
|
|
27519
|
+
verification,
|
|
27520
|
+
incremental: incrementalInfo
|
|
26879
27521
|
};
|
|
26880
27522
|
} catch (error) {
|
|
26881
27523
|
return {
|
|
@@ -26901,7 +27543,7 @@ var init_sync_service = __esm({
|
|
|
26901
27543
|
async ensureDirectories() {
|
|
26902
27544
|
const dirs = ["storage", "context", "agents", "memory", "analysis", "config", "sync"];
|
|
26903
27545
|
await Promise.all(
|
|
26904
|
-
dirs.map((dir) =>
|
|
27546
|
+
dirs.map((dir) => fs55.mkdir(path58.join(this.globalPath, dir), { recursive: true }))
|
|
26905
27547
|
);
|
|
26906
27548
|
}
|
|
26907
27549
|
// ==========================================================================
|
|
@@ -26972,7 +27614,7 @@ var init_sync_service = __esm({
|
|
|
26972
27614
|
const stats = {
|
|
26973
27615
|
fileCount: 0,
|
|
26974
27616
|
version: "0.0.0",
|
|
26975
|
-
name:
|
|
27617
|
+
name: path58.basename(this.projectPath),
|
|
26976
27618
|
ecosystem: "unknown",
|
|
26977
27619
|
projectType: "simple",
|
|
26978
27620
|
languages: [],
|
|
@@ -26989,8 +27631,8 @@ var init_sync_service = __esm({
|
|
|
26989
27631
|
stats.fileCount = 0;
|
|
26990
27632
|
}
|
|
26991
27633
|
try {
|
|
26992
|
-
const pkgPath =
|
|
26993
|
-
const pkg = JSON.parse(await
|
|
27634
|
+
const pkgPath = path58.join(this.projectPath, "package.json");
|
|
27635
|
+
const pkg = JSON.parse(await fs55.readFile(pkgPath, "utf-8"));
|
|
26994
27636
|
stats.version = pkg.version || "0.0.0";
|
|
26995
27637
|
stats.name = pkg.name || stats.name;
|
|
26996
27638
|
stats.ecosystem = "JavaScript";
|
|
@@ -27135,12 +27777,12 @@ var init_sync_service = __esm({
|
|
|
27135
27777
|
// ==========================================================================
|
|
27136
27778
|
async generateAgents(stack, stats) {
|
|
27137
27779
|
const agents = [];
|
|
27138
|
-
const agentsPath =
|
|
27780
|
+
const agentsPath = path58.join(this.globalPath, "agents");
|
|
27139
27781
|
try {
|
|
27140
|
-
const files = await
|
|
27782
|
+
const files = await fs55.readdir(agentsPath);
|
|
27141
27783
|
for (const file of files) {
|
|
27142
27784
|
if (file.endsWith(".md")) {
|
|
27143
|
-
await
|
|
27785
|
+
await fs55.unlink(path58.join(agentsPath, file));
|
|
27144
27786
|
}
|
|
27145
27787
|
}
|
|
27146
27788
|
} catch (error) {
|
|
@@ -27178,6 +27820,27 @@ var init_sync_service = __esm({
|
|
|
27178
27820
|
}
|
|
27179
27821
|
return agents;
|
|
27180
27822
|
}
|
|
27823
|
+
/**
|
|
27824
|
+
* Load existing agent info from disk (for incremental sync when agents don't need regeneration).
|
|
27825
|
+
* Reads the agents directory and returns metadata without regenerating files.
|
|
27826
|
+
*/
|
|
27827
|
+
async loadExistingAgents() {
|
|
27828
|
+
const agentsPath = path58.join(this.globalPath, "agents");
|
|
27829
|
+
const agents = [];
|
|
27830
|
+
try {
|
|
27831
|
+
const files = await fs55.readdir(agentsPath);
|
|
27832
|
+
const workflowNames = /* @__PURE__ */ new Set(["prjct-workflow", "prjct-planner", "prjct-shipper"]);
|
|
27833
|
+
for (const file of files) {
|
|
27834
|
+
if (!file.endsWith(".md")) continue;
|
|
27835
|
+
const name = file.replace(".md", "");
|
|
27836
|
+
const type = workflowNames.has(name) ? "workflow" : "domain";
|
|
27837
|
+
agents.push({ name, type });
|
|
27838
|
+
}
|
|
27839
|
+
} catch {
|
|
27840
|
+
return [];
|
|
27841
|
+
}
|
|
27842
|
+
return agents;
|
|
27843
|
+
}
|
|
27181
27844
|
/**
|
|
27182
27845
|
* Resolve {{> partial-name }} includes in template content.
|
|
27183
27846
|
* Loads partials from templates/subagents/.
|
|
@@ -27189,7 +27852,7 @@ var init_sync_service = __esm({
|
|
|
27189
27852
|
let resolved = content;
|
|
27190
27853
|
for (const match of matches) {
|
|
27191
27854
|
const partialName = match[1];
|
|
27192
|
-
const partialPath =
|
|
27855
|
+
const partialPath = path58.join(
|
|
27193
27856
|
__dirname,
|
|
27194
27857
|
"..",
|
|
27195
27858
|
"..",
|
|
@@ -27198,7 +27861,7 @@ var init_sync_service = __esm({
|
|
|
27198
27861
|
`${partialName}.md`
|
|
27199
27862
|
);
|
|
27200
27863
|
try {
|
|
27201
|
-
const partialContent = await
|
|
27864
|
+
const partialContent = await fs55.readFile(partialPath, "utf-8");
|
|
27202
27865
|
resolved = resolved.replace(match[0], partialContent.trim());
|
|
27203
27866
|
} catch {
|
|
27204
27867
|
resolved = resolved.replace(match[0], `<!-- partial "${partialName}" not found -->`);
|
|
@@ -27209,7 +27872,7 @@ var init_sync_service = __esm({
|
|
|
27209
27872
|
async generateWorkflowAgent(name, agentsPath) {
|
|
27210
27873
|
let content = "";
|
|
27211
27874
|
try {
|
|
27212
|
-
const templatePath =
|
|
27875
|
+
const templatePath = path58.join(
|
|
27213
27876
|
__dirname,
|
|
27214
27877
|
"..",
|
|
27215
27878
|
"..",
|
|
@@ -27218,7 +27881,7 @@ var init_sync_service = __esm({
|
|
|
27218
27881
|
"workflow",
|
|
27219
27882
|
`${name}.md`
|
|
27220
27883
|
);
|
|
27221
|
-
content = await
|
|
27884
|
+
content = await fs55.readFile(templatePath, "utf-8");
|
|
27222
27885
|
content = await this.resolveTemplateIncludes(content);
|
|
27223
27886
|
} catch (error) {
|
|
27224
27887
|
logger_default.debug("Workflow agent template not found, generating minimal", {
|
|
@@ -27227,12 +27890,12 @@ var init_sync_service = __esm({
|
|
|
27227
27890
|
});
|
|
27228
27891
|
content = this.generateMinimalWorkflowAgent(name);
|
|
27229
27892
|
}
|
|
27230
|
-
await
|
|
27893
|
+
await fs55.writeFile(path58.join(agentsPath, `${name}.md`), content, "utf-8");
|
|
27231
27894
|
}
|
|
27232
27895
|
async generateDomainAgent(name, agentsPath, stats, stack) {
|
|
27233
27896
|
let content = "";
|
|
27234
27897
|
try {
|
|
27235
|
-
const templatePath =
|
|
27898
|
+
const templatePath = path58.join(
|
|
27236
27899
|
__dirname,
|
|
27237
27900
|
"..",
|
|
27238
27901
|
"..",
|
|
@@ -27241,7 +27904,7 @@ var init_sync_service = __esm({
|
|
|
27241
27904
|
"domain",
|
|
27242
27905
|
`${name}.md`
|
|
27243
27906
|
);
|
|
27244
|
-
content = await
|
|
27907
|
+
content = await fs55.readFile(templatePath, "utf-8");
|
|
27245
27908
|
content = await this.resolveTemplateIncludes(content);
|
|
27246
27909
|
content = content.replace("{projectName}", stats.name);
|
|
27247
27910
|
content = content.replace("{frameworks}", stack.frameworks.join(", ") || "None detected");
|
|
@@ -27253,7 +27916,7 @@ var init_sync_service = __esm({
|
|
|
27253
27916
|
});
|
|
27254
27917
|
content = this.generateMinimalDomainAgent(name, stats, stack);
|
|
27255
27918
|
}
|
|
27256
|
-
await
|
|
27919
|
+
await fs55.writeFile(path58.join(agentsPath, `${name}.md`), content, "utf-8");
|
|
27257
27920
|
}
|
|
27258
27921
|
generateMinimalWorkflowAgent(name) {
|
|
27259
27922
|
const descriptions = {
|
|
@@ -27321,8 +27984,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
27321
27984
|
})),
|
|
27322
27985
|
agentSkillMap: Object.fromEntries(skills.map((s) => [s.agent, s.skill]))
|
|
27323
27986
|
};
|
|
27324
|
-
|
|
27325
|
-
|
|
27987
|
+
fs55.writeFile(
|
|
27988
|
+
path58.join(this.globalPath, "config", "skills.json"),
|
|
27326
27989
|
JSON.stringify(skillsConfig, null, 2),
|
|
27327
27990
|
"utf-8"
|
|
27328
27991
|
).catch((error) => {
|
|
@@ -27340,7 +28003,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
27340
28003
|
async autoInstallSkills(agents) {
|
|
27341
28004
|
const results = [];
|
|
27342
28005
|
try {
|
|
27343
|
-
const mappingsPath =
|
|
28006
|
+
const mappingsPath = path58.join(
|
|
27344
28007
|
__dirname,
|
|
27345
28008
|
"..",
|
|
27346
28009
|
"..",
|
|
@@ -27348,7 +28011,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
27348
28011
|
"config",
|
|
27349
28012
|
"skill-mappings.json"
|
|
27350
28013
|
);
|
|
27351
|
-
const mappingsContent = await
|
|
28014
|
+
const mappingsContent = await fs55.readFile(mappingsPath, "utf-8");
|
|
27352
28015
|
const mappings = JSON.parse(mappingsContent);
|
|
27353
28016
|
const agentToSkillMap = mappings.agentToSkillMap || {};
|
|
27354
28017
|
const packagesToInstall = [];
|
|
@@ -27361,18 +28024,18 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
27361
28024
|
}
|
|
27362
28025
|
}
|
|
27363
28026
|
if (packagesToInstall.length === 0) return results;
|
|
27364
|
-
const skillsDir =
|
|
28027
|
+
const skillsDir = path58.join(os16.homedir(), ".claude", "skills");
|
|
27365
28028
|
for (const { pkg, agent } of packagesToInstall) {
|
|
27366
28029
|
const skillName = pkg.split("/").pop() || pkg;
|
|
27367
|
-
const subdirPath =
|
|
27368
|
-
const flatPath =
|
|
28030
|
+
const subdirPath = path58.join(skillsDir, skillName, "SKILL.md");
|
|
28031
|
+
const flatPath = path58.join(skillsDir, `${skillName}.md`);
|
|
27369
28032
|
let alreadyInstalled = false;
|
|
27370
28033
|
try {
|
|
27371
|
-
await
|
|
28034
|
+
await fs55.access(subdirPath);
|
|
27372
28035
|
alreadyInstalled = true;
|
|
27373
28036
|
} catch {
|
|
27374
28037
|
try {
|
|
27375
|
-
await
|
|
28038
|
+
await fs55.access(flatPath);
|
|
27376
28039
|
alreadyInstalled = true;
|
|
27377
28040
|
} catch {
|
|
27378
28041
|
}
|
|
@@ -27424,10 +28087,10 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
27424
28087
|
// PROJECT.JSON UPDATE
|
|
27425
28088
|
// ==========================================================================
|
|
27426
28089
|
async updateProjectJson(git, stats) {
|
|
27427
|
-
const projectJsonPath =
|
|
28090
|
+
const projectJsonPath = path58.join(this.globalPath, "project.json");
|
|
27428
28091
|
let existing = {};
|
|
27429
28092
|
try {
|
|
27430
|
-
existing = JSON.parse(await
|
|
28093
|
+
existing = JSON.parse(await fs55.readFile(projectJsonPath, "utf-8"));
|
|
27431
28094
|
} catch (error) {
|
|
27432
28095
|
logger_default.debug("No existing project.json", {
|
|
27433
28096
|
path: projectJsonPath,
|
|
@@ -27453,16 +28116,16 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
27453
28116
|
lastSyncCommit: git.recentCommits[0]?.hash || null,
|
|
27454
28117
|
lastSyncBranch: git.branch
|
|
27455
28118
|
};
|
|
27456
|
-
await
|
|
28119
|
+
await fs55.writeFile(projectJsonPath, JSON.stringify(updated, null, 2), "utf-8");
|
|
27457
28120
|
}
|
|
27458
28121
|
// ==========================================================================
|
|
27459
28122
|
// STATE.JSON UPDATE
|
|
27460
28123
|
// ==========================================================================
|
|
27461
28124
|
async updateStateJson(stats, stack) {
|
|
27462
|
-
const statePath =
|
|
28125
|
+
const statePath = path58.join(this.globalPath, "storage", "state.json");
|
|
27463
28126
|
let state = {};
|
|
27464
28127
|
try {
|
|
27465
|
-
state = JSON.parse(await
|
|
28128
|
+
state = JSON.parse(await fs55.readFile(statePath, "utf-8"));
|
|
27466
28129
|
} catch (error) {
|
|
27467
28130
|
logger_default.debug("No existing state.json", { path: statePath, error: getErrorMessage(error) });
|
|
27468
28131
|
}
|
|
@@ -27490,7 +28153,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
27490
28153
|
lastAction: "Synced project",
|
|
27491
28154
|
nextAction: 'Run `p. task "description"` to start working'
|
|
27492
28155
|
};
|
|
27493
|
-
await
|
|
28156
|
+
await fs55.writeFile(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
27494
28157
|
try {
|
|
27495
28158
|
await localStateGenerator.generate(
|
|
27496
28159
|
this.projectPath,
|
|
@@ -27504,7 +28167,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
27504
28167
|
// MEMORY LOGGING
|
|
27505
28168
|
// ==========================================================================
|
|
27506
28169
|
async logToMemory(git, stats) {
|
|
27507
|
-
const memoryPath =
|
|
28170
|
+
const memoryPath = path58.join(this.globalPath, "memory", "events.jsonl");
|
|
27508
28171
|
const event = {
|
|
27509
28172
|
ts: getTimestamp(),
|
|
27510
28173
|
action: "sync",
|
|
@@ -27513,7 +28176,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
27513
28176
|
fileCount: stats.fileCount,
|
|
27514
28177
|
commitCount: git.commits
|
|
27515
28178
|
};
|
|
27516
|
-
await
|
|
28179
|
+
await fs55.appendFile(memoryPath, `${JSON.stringify(event)}
|
|
27517
28180
|
`, "utf-8");
|
|
27518
28181
|
}
|
|
27519
28182
|
// ==========================================================================
|
|
@@ -27533,8 +28196,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
27533
28196
|
let filteredChars = 0;
|
|
27534
28197
|
for (const file of contextFiles) {
|
|
27535
28198
|
try {
|
|
27536
|
-
const filePath =
|
|
27537
|
-
const content = await
|
|
28199
|
+
const filePath = path58.join(this.globalPath, file);
|
|
28200
|
+
const content = await fs55.readFile(filePath, "utf-8");
|
|
27538
28201
|
filteredChars += content.length;
|
|
27539
28202
|
} catch (error) {
|
|
27540
28203
|
logger_default.debug("Context file not found for metrics", { file, error: getErrorMessage(error) });
|
|
@@ -27542,8 +28205,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
27542
28205
|
}
|
|
27543
28206
|
for (const agent of agents) {
|
|
27544
28207
|
try {
|
|
27545
|
-
const agentPath =
|
|
27546
|
-
const content = await
|
|
28208
|
+
const agentPath = path58.join(this.globalPath, "agents", `${agent.name}.md`);
|
|
28209
|
+
const content = await fs55.readFile(agentPath, "utf-8");
|
|
27547
28210
|
filteredChars += content.length;
|
|
27548
28211
|
} catch (error) {
|
|
27549
28212
|
logger_default.debug("Agent file not found for metrics", {
|
|
@@ -27601,11 +28264,45 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
27601
28264
|
}
|
|
27602
28265
|
}
|
|
27603
28266
|
// ==========================================================================
|
|
28267
|
+
// ARCHIVAL (PRJ-267)
|
|
28268
|
+
// ==========================================================================
|
|
28269
|
+
/**
|
|
28270
|
+
* Archive stale data across all storage types.
|
|
28271
|
+
* Runs during sync to keep active storage lean.
|
|
28272
|
+
*/
|
|
28273
|
+
async archiveStaleData() {
|
|
28274
|
+
if (!this.projectId) return;
|
|
28275
|
+
try {
|
|
28276
|
+
const [shipped, dormant, staleQueue, stalePaused, memoryCapped] = await Promise.all([
|
|
28277
|
+
shippedStorage.archiveOldShipped(this.projectId).catch(() => 0),
|
|
28278
|
+
ideasStorage.markDormantIdeas(this.projectId).catch(() => 0),
|
|
28279
|
+
queueStorage.removeStaleCompleted(this.projectId).catch(() => 0),
|
|
28280
|
+
stateStorage.archiveStalePausedTasks(this.projectId).catch(() => []),
|
|
28281
|
+
memoryService.capEntries(this.projectId).catch(() => 0)
|
|
28282
|
+
]);
|
|
28283
|
+
const totalArchived = shipped + dormant + staleQueue + stalePaused.length + memoryCapped;
|
|
28284
|
+
if (totalArchived > 0) {
|
|
28285
|
+
logger_default.info("Archived stale data", {
|
|
28286
|
+
shipped,
|
|
28287
|
+
dormant,
|
|
28288
|
+
staleQueue,
|
|
28289
|
+
stalePaused: stalePaused.length,
|
|
28290
|
+
memoryCapped,
|
|
28291
|
+
total: totalArchived
|
|
28292
|
+
});
|
|
28293
|
+
const stats = archiveStorage.getStats(this.projectId);
|
|
28294
|
+
logger_default.debug("Archive stats", stats);
|
|
28295
|
+
}
|
|
28296
|
+
} catch (error) {
|
|
28297
|
+
logger_default.debug("Archival failed (non-critical)", { error: getErrorMessage(error) });
|
|
28298
|
+
}
|
|
28299
|
+
}
|
|
28300
|
+
// ==========================================================================
|
|
27604
28301
|
// HELPERS
|
|
27605
28302
|
// ==========================================================================
|
|
27606
28303
|
async fileExists(filename) {
|
|
27607
28304
|
try {
|
|
27608
|
-
await
|
|
28305
|
+
await fs55.access(path58.join(this.projectPath, filename));
|
|
27609
28306
|
return true;
|
|
27610
28307
|
} catch (error) {
|
|
27611
28308
|
logger_default.debug("File not found", { filename, error: getErrorMessage(error) });
|
|
@@ -27614,8 +28311,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
|
|
|
27614
28311
|
}
|
|
27615
28312
|
async getCliVersion() {
|
|
27616
28313
|
try {
|
|
27617
|
-
const pkgPath =
|
|
27618
|
-
const pkg = JSON.parse(await
|
|
28314
|
+
const pkgPath = path58.join(__dirname, "..", "..", "package.json");
|
|
28315
|
+
const pkg = JSON.parse(await fs55.readFile(pkgPath, "utf-8"));
|
|
27619
28316
|
return pkg.version || "0.0.0";
|
|
27620
28317
|
} catch (error) {
|
|
27621
28318
|
logger_default.debug("Failed to read CLI version", { error: getErrorMessage(error) });
|
|
@@ -27776,22 +28473,22 @@ __export(uninstall_exports, {
|
|
|
27776
28473
|
uninstall: () => uninstall
|
|
27777
28474
|
});
|
|
27778
28475
|
import { execSync as execSync3 } from "node:child_process";
|
|
27779
|
-
import
|
|
28476
|
+
import fs56 from "node:fs/promises";
|
|
27780
28477
|
import os17 from "node:os";
|
|
27781
|
-
import
|
|
28478
|
+
import path59 from "node:path";
|
|
27782
28479
|
import readline2 from "node:readline";
|
|
27783
28480
|
import chalk12 from "chalk";
|
|
27784
28481
|
async function getDirectorySize(dirPath) {
|
|
27785
28482
|
let totalSize = 0;
|
|
27786
28483
|
try {
|
|
27787
|
-
const entries = await
|
|
28484
|
+
const entries = await fs56.readdir(dirPath, { withFileTypes: true });
|
|
27788
28485
|
for (const entry of entries) {
|
|
27789
|
-
const entryPath =
|
|
28486
|
+
const entryPath = path59.join(dirPath, entry.name);
|
|
27790
28487
|
if (entry.isDirectory()) {
|
|
27791
28488
|
totalSize += await getDirectorySize(entryPath);
|
|
27792
28489
|
} else {
|
|
27793
28490
|
try {
|
|
27794
|
-
const stats = await
|
|
28491
|
+
const stats = await fs56.stat(entryPath);
|
|
27795
28492
|
totalSize += stats.size;
|
|
27796
28493
|
} catch {
|
|
27797
28494
|
}
|
|
@@ -27810,7 +28507,7 @@ function formatSize(bytes) {
|
|
|
27810
28507
|
}
|
|
27811
28508
|
async function countDirectoryItems(dirPath) {
|
|
27812
28509
|
try {
|
|
27813
|
-
const entries = await
|
|
28510
|
+
const entries = await fs56.readdir(dirPath, { withFileTypes: true });
|
|
27814
28511
|
return entries.filter((e) => e.isDirectory()).length;
|
|
27815
28512
|
} catch {
|
|
27816
28513
|
return 0;
|
|
@@ -27843,7 +28540,7 @@ async function gatherUninstallItems() {
|
|
|
27843
28540
|
const providerPaths = getProviderPaths();
|
|
27844
28541
|
const prjctCliPath = path_manager_default.getGlobalBasePath();
|
|
27845
28542
|
const prjctCliExists = await fileExists(prjctCliPath);
|
|
27846
|
-
const projectCount = prjctCliExists ? await countDirectoryItems(
|
|
28543
|
+
const projectCount = prjctCliExists ? await countDirectoryItems(path59.join(prjctCliPath, "projects")) : 0;
|
|
27847
28544
|
const prjctCliSize = prjctCliExists ? await getDirectorySize(prjctCliPath) : 0;
|
|
27848
28545
|
items.push({
|
|
27849
28546
|
path: prjctCliPath,
|
|
@@ -27853,12 +28550,12 @@ async function gatherUninstallItems() {
|
|
|
27853
28550
|
count: projectCount,
|
|
27854
28551
|
exists: prjctCliExists
|
|
27855
28552
|
});
|
|
27856
|
-
const claudeMdPath =
|
|
28553
|
+
const claudeMdPath = path59.join(providerPaths.claude.config, "CLAUDE.md");
|
|
27857
28554
|
const claudeMdExists = await fileExists(claudeMdPath);
|
|
27858
28555
|
let hasPrjctSection = false;
|
|
27859
28556
|
if (claudeMdExists) {
|
|
27860
28557
|
try {
|
|
27861
|
-
const content = await
|
|
28558
|
+
const content = await fs56.readFile(claudeMdPath, "utf-8");
|
|
27862
28559
|
hasPrjctSection = content.includes(PRJCT_START_MARKER) && content.includes(PRJCT_END_MARKER);
|
|
27863
28560
|
} catch {
|
|
27864
28561
|
}
|
|
@@ -27887,7 +28584,7 @@ async function gatherUninstallItems() {
|
|
|
27887
28584
|
description: "Claude router",
|
|
27888
28585
|
exists: claudeRouterExists
|
|
27889
28586
|
});
|
|
27890
|
-
const statusLinePath =
|
|
28587
|
+
const statusLinePath = path59.join(providerPaths.claude.config, "prjct-statusline.sh");
|
|
27891
28588
|
const statusLineExists = await fileExists(statusLinePath);
|
|
27892
28589
|
items.push({
|
|
27893
28590
|
path: statusLinePath,
|
|
@@ -27903,12 +28600,12 @@ async function gatherUninstallItems() {
|
|
|
27903
28600
|
description: "Gemini router",
|
|
27904
28601
|
exists: geminiRouterExists
|
|
27905
28602
|
});
|
|
27906
|
-
const geminiMdPath =
|
|
28603
|
+
const geminiMdPath = path59.join(providerPaths.gemini.config, "GEMINI.md");
|
|
27907
28604
|
const geminiMdExists = await fileExists(geminiMdPath);
|
|
27908
28605
|
let hasGeminiPrjctSection = false;
|
|
27909
28606
|
if (geminiMdExists) {
|
|
27910
28607
|
try {
|
|
27911
|
-
const content = await
|
|
28608
|
+
const content = await fs56.readFile(geminiMdPath, "utf-8");
|
|
27912
28609
|
hasGeminiPrjctSection = content.includes(PRJCT_START_MARKER) && content.includes(PRJCT_END_MARKER);
|
|
27913
28610
|
} catch {
|
|
27914
28611
|
}
|
|
@@ -27925,7 +28622,7 @@ async function gatherUninstallItems() {
|
|
|
27925
28622
|
}
|
|
27926
28623
|
async function removePrjctSection(filePath) {
|
|
27927
28624
|
try {
|
|
27928
|
-
const content = await
|
|
28625
|
+
const content = await fs56.readFile(filePath, "utf-8");
|
|
27929
28626
|
if (!content.includes(PRJCT_START_MARKER) || !content.includes(PRJCT_END_MARKER)) {
|
|
27930
28627
|
return false;
|
|
27931
28628
|
}
|
|
@@ -27934,9 +28631,9 @@ async function removePrjctSection(filePath) {
|
|
|
27934
28631
|
let newContent = content.substring(0, startIndex) + content.substring(endIndex);
|
|
27935
28632
|
newContent = newContent.replace(/\n{3,}/g, "\n\n").trim();
|
|
27936
28633
|
if (!newContent || newContent.trim().length === 0) {
|
|
27937
|
-
await
|
|
28634
|
+
await fs56.unlink(filePath);
|
|
27938
28635
|
} else {
|
|
27939
|
-
await
|
|
28636
|
+
await fs56.writeFile(filePath, `${newContent}
|
|
27940
28637
|
`, "utf-8");
|
|
27941
28638
|
}
|
|
27942
28639
|
return true;
|
|
@@ -27947,12 +28644,12 @@ async function removePrjctSection(filePath) {
|
|
|
27947
28644
|
async function createBackup3() {
|
|
27948
28645
|
const homeDir = os17.homedir();
|
|
27949
28646
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").substring(0, 19);
|
|
27950
|
-
const backupDir =
|
|
28647
|
+
const backupDir = path59.join(homeDir, `.prjct-backup-${timestamp}`);
|
|
27951
28648
|
try {
|
|
27952
|
-
await
|
|
28649
|
+
await fs56.mkdir(backupDir, { recursive: true });
|
|
27953
28650
|
const prjctCliPath = path_manager_default.getGlobalBasePath();
|
|
27954
28651
|
if (await fileExists(prjctCliPath)) {
|
|
27955
|
-
await copyDirectory(prjctCliPath,
|
|
28652
|
+
await copyDirectory(prjctCliPath, path59.join(backupDir, ".prjct-cli"));
|
|
27956
28653
|
}
|
|
27957
28654
|
return backupDir;
|
|
27958
28655
|
} catch {
|
|
@@ -27960,15 +28657,15 @@ async function createBackup3() {
|
|
|
27960
28657
|
}
|
|
27961
28658
|
}
|
|
27962
28659
|
async function copyDirectory(src, dest) {
|
|
27963
|
-
await
|
|
27964
|
-
const entries = await
|
|
28660
|
+
await fs56.mkdir(dest, { recursive: true });
|
|
28661
|
+
const entries = await fs56.readdir(src, { withFileTypes: true });
|
|
27965
28662
|
for (const entry of entries) {
|
|
27966
|
-
const srcPath =
|
|
27967
|
-
const destPath =
|
|
28663
|
+
const srcPath = path59.join(src, entry.name);
|
|
28664
|
+
const destPath = path59.join(dest, entry.name);
|
|
27968
28665
|
if (entry.isDirectory()) {
|
|
27969
28666
|
await copyDirectory(srcPath, destPath);
|
|
27970
28667
|
} else {
|
|
27971
|
-
await
|
|
28668
|
+
await fs56.copyFile(srcPath, destPath);
|
|
27972
28669
|
}
|
|
27973
28670
|
}
|
|
27974
28671
|
}
|
|
@@ -27984,10 +28681,10 @@ async function performUninstall(items, installation, options) {
|
|
|
27984
28681
|
deleted.push(item.path);
|
|
27985
28682
|
}
|
|
27986
28683
|
} else if (item.type === "directory") {
|
|
27987
|
-
await
|
|
28684
|
+
await fs56.rm(item.path, { recursive: true, force: true });
|
|
27988
28685
|
deleted.push(item.path);
|
|
27989
28686
|
} else if (item.type === "file") {
|
|
27990
|
-
await
|
|
28687
|
+
await fs56.unlink(item.path);
|
|
27991
28688
|
deleted.push(item.path);
|
|
27992
28689
|
}
|
|
27993
28690
|
} catch (error) {
|
|
@@ -28164,7 +28861,7 @@ __export(watch_service_exports, {
|
|
|
28164
28861
|
WatchService: () => WatchService,
|
|
28165
28862
|
watchService: () => watchService
|
|
28166
28863
|
});
|
|
28167
|
-
import
|
|
28864
|
+
import path60 from "node:path";
|
|
28168
28865
|
import chalk13 from "chalk";
|
|
28169
28866
|
import chokidar from "chokidar";
|
|
28170
28867
|
var TRIGGER_PATTERNS, IGNORE_PATTERNS2, WatchService, watchService;
|
|
@@ -28353,7 +29050,7 @@ ${chalk13.dim(`[${timestamp}]`)} ${chalk13.cyan("\u27F3")} ${filesSummary} chang
|
|
|
28353
29050
|
);
|
|
28354
29051
|
}
|
|
28355
29052
|
try {
|
|
28356
|
-
const result = await syncService.sync(this.projectPath);
|
|
29053
|
+
const result = await syncService.sync(this.projectPath, { changedFiles });
|
|
28357
29054
|
this.lastSyncTime = Date.now();
|
|
28358
29055
|
this.syncCount++;
|
|
28359
29056
|
if (result.success) {
|
|
@@ -28385,7 +29082,7 @@ ${chalk13.dim(`[${timestamp}]`)} ${chalk13.cyan("\u27F3")} ${filesSummary} chang
|
|
|
28385
29082
|
printStartup() {
|
|
28386
29083
|
console.log("");
|
|
28387
29084
|
console.log(chalk13.cyan("\u{1F441}\uFE0F Watching for changes..."));
|
|
28388
|
-
console.log(chalk13.dim(` Project: ${
|
|
29085
|
+
console.log(chalk13.dim(` Project: ${path60.basename(this.projectPath)}`));
|
|
28389
29086
|
console.log(chalk13.dim(` Debounce: ${this.options.debounceMs}ms`));
|
|
28390
29087
|
console.log(chalk13.dim(` Min interval: ${this.options.minIntervalMs / 1e3}s`));
|
|
28391
29088
|
console.log("");
|
|
@@ -28558,11 +29255,13 @@ var init_command_data = __esm({
|
|
|
28558
29255
|
name: "sync",
|
|
28559
29256
|
group: "core",
|
|
28560
29257
|
description: "Sync project state and update workflow agents",
|
|
28561
|
-
usage: { claude: "/p:sync", terminal: "prjct sync [--package=<name>]" },
|
|
29258
|
+
usage: { claude: "/p:sync", terminal: "prjct sync [--package=<name>] [--full]" },
|
|
28562
29259
|
implemented: true,
|
|
28563
29260
|
hasTemplate: true,
|
|
28564
29261
|
requiresProject: true,
|
|
28565
29262
|
features: [
|
|
29263
|
+
"Incremental sync: only re-analyzes changed files (default)",
|
|
29264
|
+
"Force full sync: --full bypasses incremental cache",
|
|
28566
29265
|
"Monorepo support: --package=<name> for single package sync",
|
|
28567
29266
|
"Nested PRJCT.md inheritance",
|
|
28568
29267
|
"Per-package CLAUDE.md generation"
|
|
@@ -29116,9 +29815,9 @@ __export(setup_exports, {
|
|
|
29116
29815
|
run: () => run
|
|
29117
29816
|
});
|
|
29118
29817
|
import { execSync as execSync4 } from "node:child_process";
|
|
29119
|
-
import
|
|
29818
|
+
import fs57 from "node:fs/promises";
|
|
29120
29819
|
import os18 from "node:os";
|
|
29121
|
-
import
|
|
29820
|
+
import path61 from "node:path";
|
|
29122
29821
|
import chalk15 from "chalk";
|
|
29123
29822
|
async function installAICLI(provider) {
|
|
29124
29823
|
const packageName = provider.name === "claude" ? "@anthropic-ai/claude-code" : "@google/gemini-cli";
|
|
@@ -29257,12 +29956,12 @@ async function run() {
|
|
|
29257
29956
|
}
|
|
29258
29957
|
async function installGeminiRouter() {
|
|
29259
29958
|
try {
|
|
29260
|
-
const geminiCommandsDir =
|
|
29261
|
-
const routerSource =
|
|
29262
|
-
const routerDest =
|
|
29263
|
-
await
|
|
29959
|
+
const geminiCommandsDir = path61.join(os18.homedir(), ".gemini", "commands");
|
|
29960
|
+
const routerSource = path61.join(PACKAGE_ROOT, "templates", "commands", "p.toml");
|
|
29961
|
+
const routerDest = path61.join(geminiCommandsDir, "p.toml");
|
|
29962
|
+
await fs57.mkdir(geminiCommandsDir, { recursive: true });
|
|
29264
29963
|
if (await fileExists(routerSource)) {
|
|
29265
|
-
await
|
|
29964
|
+
await fs57.copyFile(routerSource, routerDest);
|
|
29266
29965
|
return true;
|
|
29267
29966
|
}
|
|
29268
29967
|
return false;
|
|
@@ -29273,15 +29972,15 @@ async function installGeminiRouter() {
|
|
|
29273
29972
|
}
|
|
29274
29973
|
async function installGeminiGlobalConfig() {
|
|
29275
29974
|
try {
|
|
29276
|
-
const geminiDir =
|
|
29277
|
-
const globalConfigPath =
|
|
29278
|
-
const templatePath =
|
|
29279
|
-
await
|
|
29280
|
-
const templateContent = await
|
|
29975
|
+
const geminiDir = path61.join(os18.homedir(), ".gemini");
|
|
29976
|
+
const globalConfigPath = path61.join(geminiDir, "GEMINI.md");
|
|
29977
|
+
const templatePath = path61.join(PACKAGE_ROOT, "templates", "global", "GEMINI.md");
|
|
29978
|
+
await fs57.mkdir(geminiDir, { recursive: true });
|
|
29979
|
+
const templateContent = await fs57.readFile(templatePath, "utf-8");
|
|
29281
29980
|
let existingContent = "";
|
|
29282
29981
|
let configExists = false;
|
|
29283
29982
|
try {
|
|
29284
|
-
existingContent = await
|
|
29983
|
+
existingContent = await fs57.readFile(globalConfigPath, "utf-8");
|
|
29285
29984
|
configExists = true;
|
|
29286
29985
|
} catch (error) {
|
|
29287
29986
|
if (isNotFoundError(error)) {
|
|
@@ -29291,7 +29990,7 @@ async function installGeminiGlobalConfig() {
|
|
|
29291
29990
|
}
|
|
29292
29991
|
}
|
|
29293
29992
|
if (!configExists) {
|
|
29294
|
-
await
|
|
29993
|
+
await fs57.writeFile(globalConfigPath, templateContent, "utf-8");
|
|
29295
29994
|
return { success: true, action: "created" };
|
|
29296
29995
|
}
|
|
29297
29996
|
const startMarker = "<!-- prjct:start - DO NOT REMOVE THIS MARKER -->";
|
|
@@ -29301,7 +30000,7 @@ async function installGeminiGlobalConfig() {
|
|
|
29301
30000
|
const updatedContent2 = `${existingContent}
|
|
29302
30001
|
|
|
29303
30002
|
${templateContent}`;
|
|
29304
|
-
await
|
|
30003
|
+
await fs57.writeFile(globalConfigPath, updatedContent2, "utf-8");
|
|
29305
30004
|
return { success: true, action: "appended" };
|
|
29306
30005
|
}
|
|
29307
30006
|
const beforeMarker = existingContent.substring(0, existingContent.indexOf(startMarker));
|
|
@@ -29313,7 +30012,7 @@ ${templateContent}`;
|
|
|
29313
30012
|
templateContent.indexOf(endMarker) + endMarker.length
|
|
29314
30013
|
);
|
|
29315
30014
|
const updatedContent = beforeMarker + prjctSection + afterMarker;
|
|
29316
|
-
await
|
|
30015
|
+
await fs57.writeFile(globalConfigPath, updatedContent, "utf-8");
|
|
29317
30016
|
return { success: true, action: "updated" };
|
|
29318
30017
|
} catch (error) {
|
|
29319
30018
|
logger_default.warn(`Gemini config warning: ${getErrorMessage2(error)}`);
|
|
@@ -29322,18 +30021,18 @@ ${templateContent}`;
|
|
|
29322
30021
|
}
|
|
29323
30022
|
async function installAntigravitySkill() {
|
|
29324
30023
|
try {
|
|
29325
|
-
const antigravitySkillsDir =
|
|
29326
|
-
const prjctSkillDir =
|
|
29327
|
-
const skillMdPath =
|
|
29328
|
-
const templatePath =
|
|
29329
|
-
await
|
|
30024
|
+
const antigravitySkillsDir = path61.join(os18.homedir(), ".gemini", "antigravity", "skills");
|
|
30025
|
+
const prjctSkillDir = path61.join(antigravitySkillsDir, "prjct");
|
|
30026
|
+
const skillMdPath = path61.join(prjctSkillDir, "SKILL.md");
|
|
30027
|
+
const templatePath = path61.join(PACKAGE_ROOT, "templates", "antigravity", "SKILL.md");
|
|
30028
|
+
await fs57.mkdir(prjctSkillDir, { recursive: true });
|
|
29330
30029
|
const skillExists = await fileExists(skillMdPath);
|
|
29331
30030
|
if (!await fileExists(templatePath)) {
|
|
29332
30031
|
logger_default.warn("Antigravity SKILL.md template not found");
|
|
29333
30032
|
return { success: false, action: null };
|
|
29334
30033
|
}
|
|
29335
|
-
const templateContent = await
|
|
29336
|
-
await
|
|
30034
|
+
const templateContent = await fs57.readFile(templatePath, "utf-8");
|
|
30035
|
+
await fs57.writeFile(skillMdPath, templateContent, "utf-8");
|
|
29337
30036
|
return { success: true, action: skillExists ? "updated" : "created" };
|
|
29338
30037
|
} catch (error) {
|
|
29339
30038
|
logger_default.warn(`Antigravity skill warning: ${getErrorMessage2(error)}`);
|
|
@@ -29352,24 +30051,24 @@ async function installCursorProject(projectRoot) {
|
|
|
29352
30051
|
gitignoreUpdated: false
|
|
29353
30052
|
};
|
|
29354
30053
|
try {
|
|
29355
|
-
const cursorDir =
|
|
29356
|
-
const rulesDir =
|
|
29357
|
-
const commandsDir =
|
|
29358
|
-
const routerMdcDest =
|
|
29359
|
-
const routerMdcSource =
|
|
29360
|
-
const cursorCommandsSource =
|
|
29361
|
-
await
|
|
29362
|
-
await
|
|
30054
|
+
const cursorDir = path61.join(projectRoot, ".cursor");
|
|
30055
|
+
const rulesDir = path61.join(cursorDir, "rules");
|
|
30056
|
+
const commandsDir = path61.join(cursorDir, "commands");
|
|
30057
|
+
const routerMdcDest = path61.join(rulesDir, "prjct.mdc");
|
|
30058
|
+
const routerMdcSource = path61.join(PACKAGE_ROOT, "templates", "cursor", "router.mdc");
|
|
30059
|
+
const cursorCommandsSource = path61.join(PACKAGE_ROOT, "templates", "cursor", "commands");
|
|
30060
|
+
await fs57.mkdir(rulesDir, { recursive: true });
|
|
30061
|
+
await fs57.mkdir(commandsDir, { recursive: true });
|
|
29363
30062
|
if (await fileExists(routerMdcSource)) {
|
|
29364
|
-
await
|
|
30063
|
+
await fs57.copyFile(routerMdcSource, routerMdcDest);
|
|
29365
30064
|
result.rulesCreated = true;
|
|
29366
30065
|
}
|
|
29367
30066
|
if (await fileExists(cursorCommandsSource)) {
|
|
29368
|
-
const commandFiles = (await
|
|
30067
|
+
const commandFiles = (await fs57.readdir(cursorCommandsSource)).filter((f) => f.endsWith(".md"));
|
|
29369
30068
|
for (const file of commandFiles) {
|
|
29370
|
-
const src =
|
|
29371
|
-
const dest =
|
|
29372
|
-
await
|
|
30069
|
+
const src = path61.join(cursorCommandsSource, file);
|
|
30070
|
+
const dest = path61.join(commandsDir, file);
|
|
30071
|
+
await fs57.copyFile(src, dest);
|
|
29373
30072
|
}
|
|
29374
30073
|
result.commandsCreated = commandFiles.length > 0;
|
|
29375
30074
|
}
|
|
@@ -29383,7 +30082,7 @@ async function installCursorProject(projectRoot) {
|
|
|
29383
30082
|
}
|
|
29384
30083
|
async function addCursorToGitignore(projectRoot) {
|
|
29385
30084
|
try {
|
|
29386
|
-
const gitignorePath =
|
|
30085
|
+
const gitignorePath = path61.join(projectRoot, ".gitignore");
|
|
29387
30086
|
const entriesToAdd = [
|
|
29388
30087
|
"# prjct Cursor routers (regenerated per-developer)",
|
|
29389
30088
|
".cursor/rules/prjct.mdc",
|
|
@@ -29398,7 +30097,7 @@ async function addCursorToGitignore(projectRoot) {
|
|
|
29398
30097
|
let content = "";
|
|
29399
30098
|
let configExists = false;
|
|
29400
30099
|
try {
|
|
29401
|
-
content = await
|
|
30100
|
+
content = await fs57.readFile(gitignorePath, "utf-8");
|
|
29402
30101
|
configExists = true;
|
|
29403
30102
|
} catch (error) {
|
|
29404
30103
|
if (!isNotFoundError(error)) {
|
|
@@ -29413,7 +30112,7 @@ async function addCursorToGitignore(projectRoot) {
|
|
|
29413
30112
|
${entriesToAdd.join("\n")}
|
|
29414
30113
|
` : `${entriesToAdd.join("\n")}
|
|
29415
30114
|
`;
|
|
29416
|
-
await
|
|
30115
|
+
await fs57.writeFile(gitignorePath, newContent, "utf-8");
|
|
29417
30116
|
return true;
|
|
29418
30117
|
} catch (error) {
|
|
29419
30118
|
logger_default.warn(`Gitignore update warning: ${getErrorMessage2(error)}`);
|
|
@@ -29421,11 +30120,11 @@ ${entriesToAdd.join("\n")}
|
|
|
29421
30120
|
}
|
|
29422
30121
|
}
|
|
29423
30122
|
async function hasCursorProject(projectRoot) {
|
|
29424
|
-
return await fileExists(
|
|
30123
|
+
return await fileExists(path61.join(projectRoot, ".cursor"));
|
|
29425
30124
|
}
|
|
29426
30125
|
async function needsCursorRegeneration(projectRoot) {
|
|
29427
|
-
const cursorDir =
|
|
29428
|
-
const routerPath =
|
|
30126
|
+
const cursorDir = path61.join(projectRoot, ".cursor");
|
|
30127
|
+
const routerPath = path61.join(cursorDir, "rules", "prjct.mdc");
|
|
29429
30128
|
return await fileExists(cursorDir) && !await fileExists(routerPath);
|
|
29430
30129
|
}
|
|
29431
30130
|
async function installWindsurfProject(projectRoot) {
|
|
@@ -29436,26 +30135,26 @@ async function installWindsurfProject(projectRoot) {
|
|
|
29436
30135
|
gitignoreUpdated: false
|
|
29437
30136
|
};
|
|
29438
30137
|
try {
|
|
29439
|
-
const windsurfDir =
|
|
29440
|
-
const rulesDir =
|
|
29441
|
-
const workflowsDir =
|
|
29442
|
-
const routerDest =
|
|
29443
|
-
const routerSource =
|
|
29444
|
-
const windsurfWorkflowsSource =
|
|
29445
|
-
await
|
|
29446
|
-
await
|
|
30138
|
+
const windsurfDir = path61.join(projectRoot, ".windsurf");
|
|
30139
|
+
const rulesDir = path61.join(windsurfDir, "rules");
|
|
30140
|
+
const workflowsDir = path61.join(windsurfDir, "workflows");
|
|
30141
|
+
const routerDest = path61.join(rulesDir, "prjct.md");
|
|
30142
|
+
const routerSource = path61.join(PACKAGE_ROOT, "templates", "windsurf", "router.md");
|
|
30143
|
+
const windsurfWorkflowsSource = path61.join(PACKAGE_ROOT, "templates", "windsurf", "workflows");
|
|
30144
|
+
await fs57.mkdir(rulesDir, { recursive: true });
|
|
30145
|
+
await fs57.mkdir(workflowsDir, { recursive: true });
|
|
29447
30146
|
if (await fileExists(routerSource)) {
|
|
29448
|
-
await
|
|
30147
|
+
await fs57.copyFile(routerSource, routerDest);
|
|
29449
30148
|
result.rulesCreated = true;
|
|
29450
30149
|
}
|
|
29451
30150
|
if (await fileExists(windsurfWorkflowsSource)) {
|
|
29452
|
-
const workflowFiles = (await
|
|
30151
|
+
const workflowFiles = (await fs57.readdir(windsurfWorkflowsSource)).filter(
|
|
29453
30152
|
(f) => f.endsWith(".md")
|
|
29454
30153
|
);
|
|
29455
30154
|
for (const file of workflowFiles) {
|
|
29456
|
-
const src =
|
|
29457
|
-
const dest =
|
|
29458
|
-
await
|
|
30155
|
+
const src = path61.join(windsurfWorkflowsSource, file);
|
|
30156
|
+
const dest = path61.join(workflowsDir, file);
|
|
30157
|
+
await fs57.copyFile(src, dest);
|
|
29459
30158
|
}
|
|
29460
30159
|
result.workflowsCreated = workflowFiles.length > 0;
|
|
29461
30160
|
}
|
|
@@ -29469,7 +30168,7 @@ async function installWindsurfProject(projectRoot) {
|
|
|
29469
30168
|
}
|
|
29470
30169
|
async function addWindsurfToGitignore(projectRoot) {
|
|
29471
30170
|
try {
|
|
29472
|
-
const gitignorePath =
|
|
30171
|
+
const gitignorePath = path61.join(projectRoot, ".gitignore");
|
|
29473
30172
|
const entriesToAdd = [
|
|
29474
30173
|
"# prjct Windsurf routers (regenerated per-developer)",
|
|
29475
30174
|
".windsurf/rules/prjct.md",
|
|
@@ -29484,7 +30183,7 @@ async function addWindsurfToGitignore(projectRoot) {
|
|
|
29484
30183
|
let content = "";
|
|
29485
30184
|
let configExists = false;
|
|
29486
30185
|
try {
|
|
29487
|
-
content = await
|
|
30186
|
+
content = await fs57.readFile(gitignorePath, "utf-8");
|
|
29488
30187
|
configExists = true;
|
|
29489
30188
|
} catch (error) {
|
|
29490
30189
|
if (!isNotFoundError(error)) {
|
|
@@ -29499,7 +30198,7 @@ async function addWindsurfToGitignore(projectRoot) {
|
|
|
29499
30198
|
${entriesToAdd.join("\n")}
|
|
29500
30199
|
` : `${entriesToAdd.join("\n")}
|
|
29501
30200
|
`;
|
|
29502
|
-
await
|
|
30201
|
+
await fs57.writeFile(gitignorePath, newContent, "utf-8");
|
|
29503
30202
|
return true;
|
|
29504
30203
|
} catch (error) {
|
|
29505
30204
|
logger_default.warn(`Gitignore update warning: ${getErrorMessage2(error)}`);
|
|
@@ -29507,32 +30206,32 @@ ${entriesToAdd.join("\n")}
|
|
|
29507
30206
|
}
|
|
29508
30207
|
}
|
|
29509
30208
|
async function hasWindsurfProject(projectRoot) {
|
|
29510
|
-
return await fileExists(
|
|
30209
|
+
return await fileExists(path61.join(projectRoot, ".windsurf"));
|
|
29511
30210
|
}
|
|
29512
30211
|
async function needsWindsurfRegeneration(projectRoot) {
|
|
29513
|
-
const windsurfDir =
|
|
29514
|
-
const routerPath =
|
|
30212
|
+
const windsurfDir = path61.join(projectRoot, ".windsurf");
|
|
30213
|
+
const routerPath = path61.join(windsurfDir, "rules", "prjct.md");
|
|
29515
30214
|
return await fileExists(windsurfDir) && !await fileExists(routerPath);
|
|
29516
30215
|
}
|
|
29517
30216
|
async function migrateProjectsCliVersion() {
|
|
29518
30217
|
try {
|
|
29519
|
-
const projectsDir =
|
|
30218
|
+
const projectsDir = path61.join(os18.homedir(), ".prjct-cli", "projects");
|
|
29520
30219
|
if (!await fileExists(projectsDir)) {
|
|
29521
30220
|
return;
|
|
29522
30221
|
}
|
|
29523
|
-
const projectDirs = (await
|
|
30222
|
+
const projectDirs = (await fs57.readdir(projectsDir, { withFileTypes: true })).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
29524
30223
|
let migrated = 0;
|
|
29525
30224
|
for (const projectId of projectDirs) {
|
|
29526
|
-
const projectJsonPath =
|
|
30225
|
+
const projectJsonPath = path61.join(projectsDir, projectId, "project.json");
|
|
29527
30226
|
if (!await fileExists(projectJsonPath)) {
|
|
29528
30227
|
continue;
|
|
29529
30228
|
}
|
|
29530
30229
|
try {
|
|
29531
|
-
const content = await
|
|
30230
|
+
const content = await fs57.readFile(projectJsonPath, "utf8");
|
|
29532
30231
|
const project = JSON.parse(content);
|
|
29533
30232
|
if (project.cliVersion !== VERSION) {
|
|
29534
30233
|
project.cliVersion = VERSION;
|
|
29535
|
-
await
|
|
30234
|
+
await fs57.writeFile(projectJsonPath, JSON.stringify(project, null, 2));
|
|
29536
30235
|
migrated++;
|
|
29537
30236
|
}
|
|
29538
30237
|
} catch (error) {
|
|
@@ -29554,7 +30253,7 @@ async function ensureStatusLineSettings(settingsPath, statusLinePath) {
|
|
|
29554
30253
|
let settings = {};
|
|
29555
30254
|
if (await fileExists(settingsPath)) {
|
|
29556
30255
|
try {
|
|
29557
|
-
settings = JSON.parse(await
|
|
30256
|
+
settings = JSON.parse(await fs57.readFile(settingsPath, "utf8"));
|
|
29558
30257
|
} catch (error) {
|
|
29559
30258
|
if (!(error instanceof SyntaxError)) {
|
|
29560
30259
|
throw error;
|
|
@@ -29562,42 +30261,42 @@ async function ensureStatusLineSettings(settingsPath, statusLinePath) {
|
|
|
29562
30261
|
}
|
|
29563
30262
|
}
|
|
29564
30263
|
settings.statusLine = { type: "command", command: statusLinePath };
|
|
29565
|
-
await
|
|
30264
|
+
await fs57.writeFile(settingsPath, JSON.stringify(settings, null, 2));
|
|
29566
30265
|
}
|
|
29567
30266
|
async function installStatusLine() {
|
|
29568
30267
|
try {
|
|
29569
|
-
const claudeDir =
|
|
29570
|
-
const settingsPath =
|
|
29571
|
-
const claudeStatusLinePath =
|
|
29572
|
-
const prjctStatusLineDir =
|
|
29573
|
-
const prjctStatusLinePath =
|
|
29574
|
-
const prjctThemesDir =
|
|
29575
|
-
const prjctLibDir =
|
|
29576
|
-
const prjctComponentsDir =
|
|
29577
|
-
const prjctConfigPath =
|
|
29578
|
-
const assetsDir =
|
|
29579
|
-
const sourceScript =
|
|
29580
|
-
const sourceThemeDir =
|
|
29581
|
-
const sourceLibDir =
|
|
29582
|
-
const sourceComponentsDir =
|
|
29583
|
-
const sourceConfigPath =
|
|
30268
|
+
const claudeDir = path61.join(os18.homedir(), ".claude");
|
|
30269
|
+
const settingsPath = path61.join(claudeDir, "settings.json");
|
|
30270
|
+
const claudeStatusLinePath = path61.join(claudeDir, "prjct-statusline.sh");
|
|
30271
|
+
const prjctStatusLineDir = path61.join(os18.homedir(), ".prjct-cli", "statusline");
|
|
30272
|
+
const prjctStatusLinePath = path61.join(prjctStatusLineDir, "statusline.sh");
|
|
30273
|
+
const prjctThemesDir = path61.join(prjctStatusLineDir, "themes");
|
|
30274
|
+
const prjctLibDir = path61.join(prjctStatusLineDir, "lib");
|
|
30275
|
+
const prjctComponentsDir = path61.join(prjctStatusLineDir, "components");
|
|
30276
|
+
const prjctConfigPath = path61.join(prjctStatusLineDir, "config.json");
|
|
30277
|
+
const assetsDir = path61.join(PACKAGE_ROOT, "assets", "statusline");
|
|
30278
|
+
const sourceScript = path61.join(assetsDir, "statusline.sh");
|
|
30279
|
+
const sourceThemeDir = path61.join(assetsDir, "themes");
|
|
30280
|
+
const sourceLibDir = path61.join(assetsDir, "lib");
|
|
30281
|
+
const sourceComponentsDir = path61.join(assetsDir, "components");
|
|
30282
|
+
const sourceConfigPath = path61.join(assetsDir, "default-config.json");
|
|
29584
30283
|
if (!await fileExists(claudeDir)) {
|
|
29585
|
-
await
|
|
30284
|
+
await fs57.mkdir(claudeDir, { recursive: true });
|
|
29586
30285
|
}
|
|
29587
30286
|
if (!await fileExists(prjctStatusLineDir)) {
|
|
29588
|
-
await
|
|
30287
|
+
await fs57.mkdir(prjctStatusLineDir, { recursive: true });
|
|
29589
30288
|
}
|
|
29590
30289
|
if (!await fileExists(prjctThemesDir)) {
|
|
29591
|
-
await
|
|
30290
|
+
await fs57.mkdir(prjctThemesDir, { recursive: true });
|
|
29592
30291
|
}
|
|
29593
30292
|
if (!await fileExists(prjctLibDir)) {
|
|
29594
|
-
await
|
|
30293
|
+
await fs57.mkdir(prjctLibDir, { recursive: true });
|
|
29595
30294
|
}
|
|
29596
30295
|
if (!await fileExists(prjctComponentsDir)) {
|
|
29597
|
-
await
|
|
30296
|
+
await fs57.mkdir(prjctComponentsDir, { recursive: true });
|
|
29598
30297
|
}
|
|
29599
30298
|
if (await fileExists(prjctStatusLinePath)) {
|
|
29600
|
-
const existingContent = await
|
|
30299
|
+
const existingContent = await fs57.readFile(prjctStatusLinePath, "utf8");
|
|
29601
30300
|
if (existingContent.includes("CLI_VERSION=")) {
|
|
29602
30301
|
const versionMatch = existingContent.match(/CLI_VERSION="([^"]*)"/);
|
|
29603
30302
|
if (versionMatch && versionMatch[1] !== VERSION) {
|
|
@@ -29605,7 +30304,7 @@ async function installStatusLine() {
|
|
|
29605
30304
|
/CLI_VERSION="[^"]*"/,
|
|
29606
30305
|
`CLI_VERSION="${VERSION}"`
|
|
29607
30306
|
);
|
|
29608
|
-
await
|
|
30307
|
+
await fs57.writeFile(prjctStatusLinePath, updatedContent, { mode: 493 });
|
|
29609
30308
|
}
|
|
29610
30309
|
await installStatusLineModules(sourceLibDir, prjctLibDir);
|
|
29611
30310
|
await installStatusLineModules(sourceComponentsDir, prjctComponentsDir);
|
|
@@ -29615,21 +30314,21 @@ async function installStatusLine() {
|
|
|
29615
30314
|
}
|
|
29616
30315
|
}
|
|
29617
30316
|
if (await fileExists(sourceScript)) {
|
|
29618
|
-
let scriptContent = await
|
|
30317
|
+
let scriptContent = await fs57.readFile(sourceScript, "utf8");
|
|
29619
30318
|
scriptContent = scriptContent.replace(/CLI_VERSION="[^"]*"/, `CLI_VERSION="${VERSION}"`);
|
|
29620
|
-
await
|
|
30319
|
+
await fs57.writeFile(prjctStatusLinePath, scriptContent, { mode: 493 });
|
|
29621
30320
|
await installStatusLineModules(sourceLibDir, prjctLibDir);
|
|
29622
30321
|
await installStatusLineModules(sourceComponentsDir, prjctComponentsDir);
|
|
29623
30322
|
if (await fileExists(sourceThemeDir)) {
|
|
29624
|
-
const themes = await
|
|
30323
|
+
const themes = await fs57.readdir(sourceThemeDir);
|
|
29625
30324
|
for (const theme of themes) {
|
|
29626
|
-
const src =
|
|
29627
|
-
const dest =
|
|
29628
|
-
await
|
|
30325
|
+
const src = path61.join(sourceThemeDir, theme);
|
|
30326
|
+
const dest = path61.join(prjctThemesDir, theme);
|
|
30327
|
+
await fs57.copyFile(src, dest);
|
|
29629
30328
|
}
|
|
29630
30329
|
}
|
|
29631
30330
|
if (!await fileExists(prjctConfigPath) && await fileExists(sourceConfigPath)) {
|
|
29632
|
-
await
|
|
30331
|
+
await fs57.copyFile(sourceConfigPath, prjctConfigPath);
|
|
29633
30332
|
}
|
|
29634
30333
|
} else {
|
|
29635
30334
|
const scriptContent = `#!/bin/bash
|
|
@@ -29664,7 +30363,7 @@ if [ -f "$CONFIG" ]; then
|
|
|
29664
30363
|
fi
|
|
29665
30364
|
echo "prjct"
|
|
29666
30365
|
`;
|
|
29667
|
-
await
|
|
30366
|
+
await fs57.writeFile(prjctStatusLinePath, scriptContent, { mode: 493 });
|
|
29668
30367
|
}
|
|
29669
30368
|
await ensureStatusLineSymlink(claudeStatusLinePath, prjctStatusLinePath);
|
|
29670
30369
|
await ensureStatusLineSettings(settingsPath, claudeStatusLinePath);
|
|
@@ -29676,10 +30375,10 @@ echo "prjct"
|
|
|
29676
30375
|
}
|
|
29677
30376
|
async function installContext7MCP() {
|
|
29678
30377
|
try {
|
|
29679
|
-
const claudeDir =
|
|
29680
|
-
const mcpConfigPath =
|
|
30378
|
+
const claudeDir = path61.join(os18.homedir(), ".claude");
|
|
30379
|
+
const mcpConfigPath = path61.join(claudeDir, "mcp.json");
|
|
29681
30380
|
if (!await fileExists(claudeDir)) {
|
|
29682
|
-
await
|
|
30381
|
+
await fs57.mkdir(claudeDir, { recursive: true });
|
|
29683
30382
|
}
|
|
29684
30383
|
const context7Config = {
|
|
29685
30384
|
mcpServers: {
|
|
@@ -29690,16 +30389,16 @@ async function installContext7MCP() {
|
|
|
29690
30389
|
}
|
|
29691
30390
|
};
|
|
29692
30391
|
if (await fileExists(mcpConfigPath)) {
|
|
29693
|
-
const existingContent = await
|
|
30392
|
+
const existingContent = await fs57.readFile(mcpConfigPath, "utf-8");
|
|
29694
30393
|
const existingConfig = JSON.parse(existingContent);
|
|
29695
30394
|
if (existingConfig.mcpServers?.context7) {
|
|
29696
30395
|
return;
|
|
29697
30396
|
}
|
|
29698
30397
|
existingConfig.mcpServers = existingConfig.mcpServers || {};
|
|
29699
30398
|
existingConfig.mcpServers.context7 = context7Config.mcpServers.context7;
|
|
29700
|
-
await
|
|
30399
|
+
await fs57.writeFile(mcpConfigPath, JSON.stringify(existingConfig, null, 2), "utf-8");
|
|
29701
30400
|
} else {
|
|
29702
|
-
await
|
|
30401
|
+
await fs57.writeFile(mcpConfigPath, JSON.stringify(context7Config, null, 2), "utf-8");
|
|
29703
30402
|
}
|
|
29704
30403
|
} catch (error) {
|
|
29705
30404
|
logger_default.warn(`Context7 MCP setup warning: ${getErrorMessage2(error)}`);
|
|
@@ -29709,34 +30408,34 @@ async function installStatusLineModules(sourceDir, destDir) {
|
|
|
29709
30408
|
if (!await fileExists(sourceDir)) {
|
|
29710
30409
|
return;
|
|
29711
30410
|
}
|
|
29712
|
-
const files = await
|
|
30411
|
+
const files = await fs57.readdir(sourceDir);
|
|
29713
30412
|
for (const file of files) {
|
|
29714
30413
|
if (file.endsWith(".sh")) {
|
|
29715
|
-
const src =
|
|
29716
|
-
const dest =
|
|
29717
|
-
await
|
|
29718
|
-
await
|
|
30414
|
+
const src = path61.join(sourceDir, file);
|
|
30415
|
+
const dest = path61.join(destDir, file);
|
|
30416
|
+
await fs57.copyFile(src, dest);
|
|
30417
|
+
await fs57.chmod(dest, 493);
|
|
29719
30418
|
}
|
|
29720
30419
|
}
|
|
29721
30420
|
}
|
|
29722
30421
|
async function ensureStatusLineSymlink(linkPath, targetPath) {
|
|
29723
30422
|
try {
|
|
29724
30423
|
if (await fileExists(linkPath)) {
|
|
29725
|
-
const stats = await
|
|
30424
|
+
const stats = await fs57.lstat(linkPath);
|
|
29726
30425
|
if (stats.isSymbolicLink()) {
|
|
29727
|
-
const existingTarget = await
|
|
30426
|
+
const existingTarget = await fs57.readlink(linkPath);
|
|
29728
30427
|
if (existingTarget === targetPath) {
|
|
29729
30428
|
return;
|
|
29730
30429
|
}
|
|
29731
30430
|
}
|
|
29732
|
-
await
|
|
30431
|
+
await fs57.unlink(linkPath);
|
|
29733
30432
|
}
|
|
29734
|
-
await
|
|
30433
|
+
await fs57.symlink(targetPath, linkPath);
|
|
29735
30434
|
} catch (_error) {
|
|
29736
30435
|
try {
|
|
29737
30436
|
if (await fileExists(targetPath)) {
|
|
29738
|
-
await
|
|
29739
|
-
await
|
|
30437
|
+
await fs57.copyFile(targetPath, linkPath);
|
|
30438
|
+
await fs57.chmod(linkPath, 493);
|
|
29740
30439
|
}
|
|
29741
30440
|
} catch (copyError) {
|
|
29742
30441
|
if (!isNotFoundError(copyError)) {
|
|
@@ -30151,7 +30850,7 @@ var init_registry2 = __esm({
|
|
|
30151
30850
|
});
|
|
30152
30851
|
|
|
30153
30852
|
// core/commands/analytics.ts
|
|
30154
|
-
import
|
|
30853
|
+
import path62 from "node:path";
|
|
30155
30854
|
var AnalyticsCommands;
|
|
30156
30855
|
var init_analytics = __esm({
|
|
30157
30856
|
"core/commands/analytics.ts"() {
|
|
@@ -30178,7 +30877,7 @@ var init_analytics = __esm({
|
|
|
30178
30877
|
output_default.failWithHint("NO_PROJECT_ID");
|
|
30179
30878
|
return { success: false, error: "No project ID found" };
|
|
30180
30879
|
}
|
|
30181
|
-
const projectName =
|
|
30880
|
+
const projectName = path62.basename(projectPath);
|
|
30182
30881
|
const currentTask = await stateStorage.getCurrentTask(projectId);
|
|
30183
30882
|
const queueTasks = await queueStorage.getActiveTasks(projectId);
|
|
30184
30883
|
const shipped = await shippedStorage.getRecent(projectId, 5);
|
|
@@ -30430,8 +31129,8 @@ ${"\u2550".repeat(50)}
|
|
|
30430
31129
|
});
|
|
30431
31130
|
|
|
30432
31131
|
// core/commands/context.ts
|
|
30433
|
-
import
|
|
30434
|
-
import
|
|
31132
|
+
import fs58 from "node:fs/promises";
|
|
31133
|
+
import path63 from "node:path";
|
|
30435
31134
|
var ContextCommands, contextCommands;
|
|
30436
31135
|
var init_context = __esm({
|
|
30437
31136
|
"core/commands/context.ts"() {
|
|
@@ -30557,8 +31256,8 @@ var init_context = __esm({
|
|
|
30557
31256
|
*/
|
|
30558
31257
|
async loadRepoAnalysis(globalPath) {
|
|
30559
31258
|
try {
|
|
30560
|
-
const analysisPath =
|
|
30561
|
-
const content = await
|
|
31259
|
+
const analysisPath = path63.join(globalPath, "analysis", "repo-analysis.json");
|
|
31260
|
+
const content = await fs58.readFile(analysisPath, "utf-8");
|
|
30562
31261
|
const data = JSON.parse(content);
|
|
30563
31262
|
return {
|
|
30564
31263
|
ecosystem: data.ecosystem || "unknown",
|
|
@@ -30577,7 +31276,7 @@ var init_context = __esm({
|
|
|
30577
31276
|
});
|
|
30578
31277
|
|
|
30579
31278
|
// core/commands/cleanup.ts
|
|
30580
|
-
import
|
|
31279
|
+
import path64 from "node:path";
|
|
30581
31280
|
async function cleanupMemory(projectPath) {
|
|
30582
31281
|
const projectId = await config_manager_default.getProjectId(projectPath);
|
|
30583
31282
|
const results = { rotated: [], totalSize: 0, freedSpace: 0 };
|
|
@@ -30593,7 +31292,7 @@ async function cleanupMemory(projectPath) {
|
|
|
30593
31292
|
results.totalSize += sizeMB;
|
|
30594
31293
|
const rotated = await jsonl_helper_exports.rotateJsonLinesIfNeeded(filePath, 10);
|
|
30595
31294
|
if (rotated) {
|
|
30596
|
-
results.rotated.push(
|
|
31295
|
+
results.rotated.push(path64.basename(filePath));
|
|
30597
31296
|
results.freedSpace += sizeMB;
|
|
30598
31297
|
}
|
|
30599
31298
|
}
|
|
@@ -30700,7 +31399,7 @@ var init_cleanup = __esm({
|
|
|
30700
31399
|
});
|
|
30701
31400
|
|
|
30702
31401
|
// core/commands/design.ts
|
|
30703
|
-
import
|
|
31402
|
+
import path65 from "node:path";
|
|
30704
31403
|
async function design(target = null, options = {}, projectPath = process.cwd()) {
|
|
30705
31404
|
try {
|
|
30706
31405
|
const designType = options.type || "architecture";
|
|
@@ -30712,7 +31411,7 @@ async function design(target = null, options = {}, projectPath = process.cwd())
|
|
|
30712
31411
|
const designTarget = target || "system";
|
|
30713
31412
|
output_default.spin(`designing ${designType}...`);
|
|
30714
31413
|
const projectId = await config_manager_default.getProjectId(projectPath);
|
|
30715
|
-
const designsPath =
|
|
31414
|
+
const designsPath = path65.join(
|
|
30716
31415
|
path_manager_default.getGlobalProjectPath(projectId),
|
|
30717
31416
|
"planning",
|
|
30718
31417
|
"designs"
|
|
@@ -30752,7 +31451,7 @@ async function design(target = null, options = {}, projectPath = process.cwd())
|
|
|
30752
31451
|
break;
|
|
30753
31452
|
}
|
|
30754
31453
|
const designFileName = `${designType}-${designTarget.toLowerCase().replace(/\s+/g, "-")}.md`;
|
|
30755
|
-
const designFilePath =
|
|
31454
|
+
const designFilePath = path65.join(designsPath, designFileName);
|
|
30756
31455
|
await file_helper_exports.writeFile(designFilePath, designContent);
|
|
30757
31456
|
await memoryService.log(projectPath, "design_created", {
|
|
30758
31457
|
type: designType,
|
|
@@ -30777,7 +31476,7 @@ var init_design = __esm({
|
|
|
30777
31476
|
});
|
|
30778
31477
|
|
|
30779
31478
|
// core/commands/snapshots.ts
|
|
30780
|
-
import
|
|
31479
|
+
import path66 from "node:path";
|
|
30781
31480
|
async function recover(projectPath = process.cwd()) {
|
|
30782
31481
|
try {
|
|
30783
31482
|
const projectId = await config_manager_default.getProjectId(projectPath);
|
|
@@ -30829,7 +31528,7 @@ async function undo(projectPath = process.cwd()) {
|
|
|
30829
31528
|
output_default.failWithHint("NO_PROJECT_ID");
|
|
30830
31529
|
return { success: false, error: "No project ID found" };
|
|
30831
31530
|
}
|
|
30832
|
-
const snapshotsPath =
|
|
31531
|
+
const snapshotsPath = path66.join(path_manager_default.getGlobalProjectPath(projectId), "snapshots");
|
|
30833
31532
|
await file_helper_exports.ensureDir(snapshotsPath);
|
|
30834
31533
|
const { execSync: execSync5 } = await import("node:child_process");
|
|
30835
31534
|
try {
|
|
@@ -30847,7 +31546,7 @@ async function undo(projectPath = process.cwd()) {
|
|
|
30847
31546
|
cwd: projectPath,
|
|
30848
31547
|
encoding: "utf-8"
|
|
30849
31548
|
});
|
|
30850
|
-
const snapshotFile =
|
|
31549
|
+
const snapshotFile = path66.join(snapshotsPath, "history.json");
|
|
30851
31550
|
let history2 = { snapshots: [], current: -1 };
|
|
30852
31551
|
try {
|
|
30853
31552
|
const content = await file_helper_exports.readFile(snapshotFile);
|
|
@@ -30887,8 +31586,8 @@ async function redo(projectPath = process.cwd()) {
|
|
|
30887
31586
|
output_default.failWithHint("NO_PROJECT_ID");
|
|
30888
31587
|
return { success: false, error: "No project ID found" };
|
|
30889
31588
|
}
|
|
30890
|
-
const snapshotsPath =
|
|
30891
|
-
const snapshotFile =
|
|
31589
|
+
const snapshotsPath = path66.join(path_manager_default.getGlobalProjectPath(projectId), "snapshots");
|
|
31590
|
+
const snapshotFile = path66.join(snapshotsPath, "history.json");
|
|
30892
31591
|
let history2;
|
|
30893
31592
|
try {
|
|
30894
31593
|
const content = await file_helper_exports.readFile(snapshotFile);
|
|
@@ -30947,8 +31646,8 @@ async function history(projectPath = process.cwd()) {
|
|
|
30947
31646
|
output_default.failWithHint("NO_PROJECT_ID");
|
|
30948
31647
|
return { success: false, error: "No project ID found" };
|
|
30949
31648
|
}
|
|
30950
|
-
const snapshotsPath =
|
|
30951
|
-
const snapshotFile =
|
|
31649
|
+
const snapshotsPath = path66.join(path_manager_default.getGlobalProjectPath(projectId), "snapshots");
|
|
31650
|
+
const snapshotFile = path66.join(snapshotsPath, "history.json");
|
|
30952
31651
|
let snapshotHistory;
|
|
30953
31652
|
try {
|
|
30954
31653
|
const content = await file_helper_exports.readFile(snapshotFile);
|
|
@@ -31151,8 +31850,8 @@ ${chalk16.cyan("Performance Report")} ${chalk16.dim(`(last ${days} days)`)}`);
|
|
|
31151
31850
|
});
|
|
31152
31851
|
|
|
31153
31852
|
// core/commands/setup.ts
|
|
31154
|
-
import
|
|
31155
|
-
import
|
|
31853
|
+
import fs59 from "node:fs/promises";
|
|
31854
|
+
import path67 from "node:path";
|
|
31156
31855
|
import chalk17 from "chalk";
|
|
31157
31856
|
var SetupCommands;
|
|
31158
31857
|
var init_setup2 = __esm({
|
|
@@ -31281,7 +31980,7 @@ Please install it first:
|
|
|
31281
31980
|
try {
|
|
31282
31981
|
const claudeDir = path_manager_default.getClaudeDir();
|
|
31283
31982
|
const settingsPath = path_manager_default.getClaudeSettingsPath();
|
|
31284
|
-
const statusLinePath =
|
|
31983
|
+
const statusLinePath = path67.join(claudeDir, "prjct-statusline.sh");
|
|
31285
31984
|
const scriptContent = `#!/bin/bash
|
|
31286
31985
|
# prjct Status Line for Claude Code
|
|
31287
31986
|
# Shows version update notifications and current task
|
|
@@ -31339,11 +32038,11 @@ fi
|
|
|
31339
32038
|
# Default: show prjct branding
|
|
31340
32039
|
echo "\u26A1 prjct"
|
|
31341
32040
|
`;
|
|
31342
|
-
await
|
|
32041
|
+
await fs59.writeFile(statusLinePath, scriptContent, { mode: 493 });
|
|
31343
32042
|
let settings = {};
|
|
31344
32043
|
if (await fileExists(settingsPath)) {
|
|
31345
32044
|
try {
|
|
31346
|
-
settings = JSON.parse(await
|
|
32045
|
+
settings = JSON.parse(await fs59.readFile(settingsPath, "utf8"));
|
|
31347
32046
|
} catch (_error) {
|
|
31348
32047
|
}
|
|
31349
32048
|
}
|
|
@@ -31351,7 +32050,7 @@ echo "\u26A1 prjct"
|
|
|
31351
32050
|
type: "command",
|
|
31352
32051
|
command: statusLinePath
|
|
31353
32052
|
};
|
|
31354
|
-
await
|
|
32053
|
+
await fs59.writeFile(settingsPath, JSON.stringify(settings, null, 2));
|
|
31355
32054
|
return { success: true };
|
|
31356
32055
|
} catch (error) {
|
|
31357
32056
|
return { success: false, error: getErrorMessage2(error) };
|
|
@@ -31407,18 +32106,18 @@ echo "\u26A1 prjct"
|
|
|
31407
32106
|
});
|
|
31408
32107
|
|
|
31409
32108
|
// core/utils/project-commands.ts
|
|
31410
|
-
import
|
|
32109
|
+
import path68 from "node:path";
|
|
31411
32110
|
async function detectPackageManager(projectPath, pkg) {
|
|
31412
32111
|
const declared = pkg?.packageManager?.trim().toLowerCase();
|
|
31413
32112
|
if (declared?.startsWith("pnpm@")) return "pnpm";
|
|
31414
32113
|
if (declared?.startsWith("yarn@")) return "yarn";
|
|
31415
32114
|
if (declared?.startsWith("bun@")) return "bun";
|
|
31416
32115
|
if (declared?.startsWith("npm@")) return "npm";
|
|
31417
|
-
if (await fileExists2(
|
|
31418
|
-
if (await fileExists2(
|
|
31419
|
-
if (await fileExists2(
|
|
31420
|
-
if (await fileExists2(
|
|
31421
|
-
if (await fileExists2(
|
|
32116
|
+
if (await fileExists2(path68.join(projectPath, "pnpm-lock.yaml"))) return "pnpm";
|
|
32117
|
+
if (await fileExists2(path68.join(projectPath, "yarn.lock"))) return "yarn";
|
|
32118
|
+
if (await fileExists2(path68.join(projectPath, "bun.lockb"))) return "bun";
|
|
32119
|
+
if (await fileExists2(path68.join(projectPath, "bun.lock"))) return "bun";
|
|
32120
|
+
if (await fileExists2(path68.join(projectPath, "package-lock.json"))) return "npm";
|
|
31422
32121
|
return "npm";
|
|
31423
32122
|
}
|
|
31424
32123
|
function pmRun(pm, scriptName) {
|
|
@@ -31434,7 +32133,7 @@ function pmTest(pm) {
|
|
|
31434
32133
|
return "npm test";
|
|
31435
32134
|
}
|
|
31436
32135
|
async function detectProjectCommands(projectPath) {
|
|
31437
|
-
const pkgPath =
|
|
32136
|
+
const pkgPath = path68.join(projectPath, "package.json");
|
|
31438
32137
|
const pkg = await readJson(pkgPath, null);
|
|
31439
32138
|
if (pkg) {
|
|
31440
32139
|
const pm = await detectPackageManager(projectPath, pkg);
|
|
@@ -31451,27 +32150,27 @@ async function detectProjectCommands(projectPath) {
|
|
|
31451
32150
|
}
|
|
31452
32151
|
return result;
|
|
31453
32152
|
}
|
|
31454
|
-
if (await fileExists2(
|
|
32153
|
+
if (await fileExists2(path68.join(projectPath, "pytest.ini"))) {
|
|
31455
32154
|
return { stack: "python", test: { tool: "pytest", command: "pytest" } };
|
|
31456
32155
|
}
|
|
31457
|
-
const pyproject = await readFile(
|
|
32156
|
+
const pyproject = await readFile(path68.join(projectPath, "pyproject.toml"), "");
|
|
31458
32157
|
if (pyproject.includes("[tool.pytest") || pyproject.includes("pytest")) {
|
|
31459
32158
|
return { stack: "python", test: { tool: "pytest", command: "pytest" } };
|
|
31460
32159
|
}
|
|
31461
|
-
if (await fileExists2(
|
|
32160
|
+
if (await fileExists2(path68.join(projectPath, "Cargo.toml"))) {
|
|
31462
32161
|
return { stack: "rust", test: { tool: "cargo", command: "cargo test" } };
|
|
31463
32162
|
}
|
|
31464
|
-
if (await fileExists2(
|
|
32163
|
+
if (await fileExists2(path68.join(projectPath, "go.mod"))) {
|
|
31465
32164
|
return { stack: "go", test: { tool: "go", command: "go test ./..." } };
|
|
31466
32165
|
}
|
|
31467
32166
|
const files = await listFiles(projectPath);
|
|
31468
32167
|
if (files.some((f) => f.endsWith(".sln") || f.endsWith(".csproj") || f.endsWith(".fsproj"))) {
|
|
31469
32168
|
return { stack: "dotnet", test: { tool: "dotnet", command: "dotnet test" } };
|
|
31470
32169
|
}
|
|
31471
|
-
if (await fileExists2(
|
|
32170
|
+
if (await fileExists2(path68.join(projectPath, "pom.xml"))) {
|
|
31472
32171
|
return { stack: "java", test: { tool: "maven", command: "mvn test" } };
|
|
31473
32172
|
}
|
|
31474
|
-
if (await fileExists2(
|
|
32173
|
+
if (await fileExists2(path68.join(projectPath, "gradlew")) && (await fileExists2(path68.join(projectPath, "build.gradle")) || await fileExists2(path68.join(projectPath, "build.gradle.kts")))) {
|
|
31475
32174
|
return { stack: "java", test: { tool: "gradle", command: "./gradlew test" } };
|
|
31476
32175
|
}
|
|
31477
32176
|
return { stack: "unknown" };
|
|
@@ -31646,7 +32345,7 @@ var init_workflow_preferences = __esm({
|
|
|
31646
32345
|
});
|
|
31647
32346
|
|
|
31648
32347
|
// core/commands/shipping.ts
|
|
31649
|
-
import
|
|
32348
|
+
import path69 from "node:path";
|
|
31650
32349
|
var ShippingCommands;
|
|
31651
32350
|
var init_shipping = __esm({
|
|
31652
32351
|
"core/commands/shipping.ts"() {
|
|
@@ -31792,7 +32491,7 @@ ${result.stderr}`.trim();
|
|
|
31792
32491
|
*/
|
|
31793
32492
|
async _bumpVersion(projectPath) {
|
|
31794
32493
|
try {
|
|
31795
|
-
const pkgPath =
|
|
32494
|
+
const pkgPath = path69.join(projectPath, "package.json");
|
|
31796
32495
|
const pkg = await file_helper_exports.readJson(pkgPath, { version: "0.0.0" });
|
|
31797
32496
|
const oldVersion = pkg?.version || "0.0.0";
|
|
31798
32497
|
const [major, minor, patch] = oldVersion.split(".").map(Number);
|
|
@@ -31814,7 +32513,7 @@ ${result.stderr}`.trim();
|
|
|
31814
32513
|
*/
|
|
31815
32514
|
async _updateChangelog(feature, version, projectPath) {
|
|
31816
32515
|
try {
|
|
31817
|
-
const changelogPath =
|
|
32516
|
+
const changelogPath = path69.join(projectPath, "CHANGELOG.md");
|
|
31818
32517
|
const changelog = await file_helper_exports.readFile(changelogPath, "# Changelog\n\n");
|
|
31819
32518
|
const entry = `## [${version}] - ${date_helper_exports.formatDate(/* @__PURE__ */ new Date())}
|
|
31820
32519
|
|
|
@@ -32858,11 +33557,11 @@ var init_linear = __esm({
|
|
|
32858
33557
|
});
|
|
32859
33558
|
|
|
32860
33559
|
// core/utils/project-credentials.ts
|
|
32861
|
-
import
|
|
33560
|
+
import fs60 from "node:fs/promises";
|
|
32862
33561
|
import os19 from "node:os";
|
|
32863
|
-
import
|
|
33562
|
+
import path70 from "node:path";
|
|
32864
33563
|
function getCredentialsPath(projectId) {
|
|
32865
|
-
return
|
|
33564
|
+
return path70.join(os19.homedir(), ".prjct-cli", "projects", projectId, "config", "credentials.json");
|
|
32866
33565
|
}
|
|
32867
33566
|
async function getProjectCredentials(projectId) {
|
|
32868
33567
|
const credPath = getCredentialsPath(projectId);
|
|
@@ -32870,7 +33569,7 @@ async function getProjectCredentials(projectId) {
|
|
|
32870
33569
|
return {};
|
|
32871
33570
|
}
|
|
32872
33571
|
try {
|
|
32873
|
-
return JSON.parse(await
|
|
33572
|
+
return JSON.parse(await fs60.readFile(credPath, "utf-8"));
|
|
32874
33573
|
} catch (error) {
|
|
32875
33574
|
console.error("[project-credentials] Failed to read credentials:", getErrorMessage2(error));
|
|
32876
33575
|
return {};
|
|
@@ -33542,7 +34241,7 @@ var require_package = __commonJS({
|
|
|
33542
34241
|
"package.json"(exports, module) {
|
|
33543
34242
|
module.exports = {
|
|
33544
34243
|
name: "prjct-cli",
|
|
33545
|
-
version: "1.
|
|
34244
|
+
version: "1.19.0",
|
|
33546
34245
|
description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
|
|
33547
34246
|
main: "core/index.ts",
|
|
33548
34247
|
bin: {
|
|
@@ -33649,7 +34348,7 @@ var require_package = __commonJS({
|
|
|
33649
34348
|
// core/index.ts
|
|
33650
34349
|
var core_exports = {};
|
|
33651
34350
|
import os20 from "node:os";
|
|
33652
|
-
import
|
|
34351
|
+
import path71 from "node:path";
|
|
33653
34352
|
import chalk20 from "chalk";
|
|
33654
34353
|
async function main() {
|
|
33655
34354
|
const [commandName, ...rawArgs] = process.argv.slice(2);
|
|
@@ -33754,7 +34453,8 @@ async function main() {
|
|
|
33754
34453
|
preview: options.preview === true || options["dry-run"] === true,
|
|
33755
34454
|
yes: options.yes === true,
|
|
33756
34455
|
json: options.json === true,
|
|
33757
|
-
package: options.package ? String(options.package) : void 0
|
|
34456
|
+
package: options.package ? String(options.package) : void 0,
|
|
34457
|
+
full: options.full === true
|
|
33758
34458
|
}), "sync"),
|
|
33759
34459
|
seal: /* @__PURE__ */ __name(() => commands.seal(process.cwd(), { json: options.json === true }), "seal"),
|
|
33760
34460
|
verify: /* @__PURE__ */ __name(() => commands.verify(process.cwd(), { json: options.json === true }), "verify"),
|
|
@@ -33862,13 +34562,13 @@ function parseCommandArgs(_cmd, rawArgs) {
|
|
|
33862
34562
|
}
|
|
33863
34563
|
async function displayVersion(version) {
|
|
33864
34564
|
const detection = await detectAllProviders();
|
|
33865
|
-
const claudeCommandPath =
|
|
33866
|
-
const geminiCommandPath =
|
|
34565
|
+
const claudeCommandPath = path71.join(os20.homedir(), ".claude", "commands", "p.md");
|
|
34566
|
+
const geminiCommandPath = path71.join(os20.homedir(), ".gemini", "commands", "p.toml");
|
|
33867
34567
|
const [claudeConfigured, geminiConfigured, cursorConfigured, cursorExists] = await Promise.all([
|
|
33868
34568
|
fileExists(claudeCommandPath),
|
|
33869
34569
|
fileExists(geminiCommandPath),
|
|
33870
|
-
fileExists(
|
|
33871
|
-
fileExists(
|
|
34570
|
+
fileExists(path71.join(process.cwd(), ".cursor", "commands", "sync.md")),
|
|
34571
|
+
fileExists(path71.join(process.cwd(), ".cursor"))
|
|
33872
34572
|
]);
|
|
33873
34573
|
const antigravityDetection = await detectAntigravity();
|
|
33874
34574
|
console.log(`
|
|
@@ -34007,7 +34707,7 @@ init_ai_provider();
|
|
|
34007
34707
|
init_config_manager();
|
|
34008
34708
|
init_editors_config();
|
|
34009
34709
|
import os21 from "node:os";
|
|
34010
|
-
import
|
|
34710
|
+
import path72 from "node:path";
|
|
34011
34711
|
import chalk21 from "chalk";
|
|
34012
34712
|
|
|
34013
34713
|
// core/server/server.ts
|
|
@@ -34828,13 +35528,13 @@ async function checkRoutersInstalled() {
|
|
|
34828
35528
|
const home = os21.homedir();
|
|
34829
35529
|
const detection = await detectAllProviders();
|
|
34830
35530
|
if (detection.claude.installed) {
|
|
34831
|
-
const claudeRouter =
|
|
35531
|
+
const claudeRouter = path72.join(home, ".claude", "commands", "p.md");
|
|
34832
35532
|
if (!await fileExists(claudeRouter)) {
|
|
34833
35533
|
return false;
|
|
34834
35534
|
}
|
|
34835
35535
|
}
|
|
34836
35536
|
if (detection.gemini.installed) {
|
|
34837
|
-
const geminiRouter =
|
|
35537
|
+
const geminiRouter = path72.join(home, ".gemini", "commands", "p.toml");
|
|
34838
35538
|
if (!await fileExists(geminiRouter)) {
|
|
34839
35539
|
return false;
|
|
34840
35540
|
}
|
|
@@ -34975,7 +35675,7 @@ if (args[0] === "start" || args[0] === "setup") {
|
|
|
34975
35675
|
console.error('No prjct project found. Run "prjct init" first.');
|
|
34976
35676
|
process.exitCode = 1;
|
|
34977
35677
|
} else {
|
|
34978
|
-
const linearCliPath =
|
|
35678
|
+
const linearCliPath = path72.join(__dirname, "..", "core", "cli", "linear.ts");
|
|
34979
35679
|
const linearArgs = ["--project", projectId, ...args.slice(1)];
|
|
34980
35680
|
const child = spawn("bun", [linearCliPath, ...linearArgs], {
|
|
34981
35681
|
stdio: "inherit",
|
|
@@ -35002,12 +35702,12 @@ if (args[0] === "start" || args[0] === "setup") {
|
|
|
35002
35702
|
windsurfDetected,
|
|
35003
35703
|
windsurfConfigured
|
|
35004
35704
|
] = await Promise.all([
|
|
35005
|
-
fileExists(
|
|
35006
|
-
fileExists(
|
|
35007
|
-
fileExists(
|
|
35008
|
-
fileExists(
|
|
35009
|
-
fileExists(
|
|
35010
|
-
fileExists(
|
|
35705
|
+
fileExists(path72.join(home, ".claude", "commands", "p.md")),
|
|
35706
|
+
fileExists(path72.join(home, ".gemini", "commands", "p.toml")),
|
|
35707
|
+
fileExists(path72.join(cwd, ".cursor")),
|
|
35708
|
+
fileExists(path72.join(cwd, ".cursor", "rules", "prjct.mdc")),
|
|
35709
|
+
fileExists(path72.join(cwd, ".windsurf")),
|
|
35710
|
+
fileExists(path72.join(cwd, ".windsurf", "rules", "prjct.md"))
|
|
35011
35711
|
]);
|
|
35012
35712
|
console.log(`
|
|
35013
35713
|
${chalk21.cyan("p/")} prjct v${VERSION}
|
|
@@ -35046,7 +35746,7 @@ ${chalk21.dim("Run 'prjct init' to configure (Cursor/Windsurf IDE)")}
|
|
|
35046
35746
|
${chalk21.cyan("https://prjct.app")}
|
|
35047
35747
|
`);
|
|
35048
35748
|
} else {
|
|
35049
|
-
const configPath =
|
|
35749
|
+
const configPath = path72.join(os21.homedir(), ".prjct-cli", "config", "installed-editors.json");
|
|
35050
35750
|
const routersInstalled = await checkRoutersInstalled();
|
|
35051
35751
|
if (!await fileExists(configPath) || !routersInstalled) {
|
|
35052
35752
|
console.log(`
|