@orqint/cli 0.1.2 → 0.2.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/README.md CHANGED
@@ -14,6 +14,29 @@ npx --yes @orqint/cli@latest init
14
14
  2. It also installs **user assets** when missing: **`AGENTS.md`**, **`ORQINT.md`**, and **`.cursor/rules/orqint-workflow.mdc`** (so Cursor can help with handoffs). Existing files are **not** overwritten — use **`orqint init --force-user-assets`** only if you want to refresh those from the package.
15
15
  3. Open **`ORQINT.md`** in your repo for the short workflow (commands and tables).
16
16
 
17
+ ### After `npm update @orqint/cli` (safe template refresh)
18
+
19
+ Orqint records a **lock** at **`orqint/core/user-assets.lock.json`** for each managed file it wrote. **`orqint sync-assets`** replaces those files from the **installed** package only when the file on disk still matches the lock (so your edits are never silently overwritten). It does **not** reset **`memory.json`**.
20
+
21
+ ```bash
22
+ npm update @orqint/cli
23
+ orqint sync-assets
24
+ ```
25
+
26
+ If your global `orqint` is older than the package you want, run the latest CLI and sync in one step:
27
+
28
+ ```bash
29
+ orqint update --install sync-assets
30
+ ```
31
+
32
+ (Uses `npx @orqint/cli@latest sync-assets`. On Windows, Node runs this with `shell: true` so `npx` resolves like other npm commands.)
33
+
34
+ - **`orqint sync-assets --dry-run`** — print what would happen.
35
+ - **`orqint sync-assets --migrate`** — one-shot: replace all managed templates from the current package and write the lock (for projects created before the lockfile existed).
36
+ - **`orqint sync-assets --force`** — overwrite all managed files and refresh the lock (same files as **`init --force-user-assets`**, without touching memory).
37
+
38
+ **Teams:** commit **`orqint/core/user-assets.lock.json`** so everyone shares the same “managed vs customized” baseline. Without it, `sync-assets` stays conservative for files that exist but have no lock entry until someone runs **`--migrate`** once.
39
+
17
40
  Other commands (after init):
18
41
 
19
42
  ```bash
@@ -37,6 +37,7 @@ exports.runInit = runInit;
37
37
  const path = __importStar(require("path"));
38
38
  const fs = __importStar(require("fs-extra"));
39
39
  const paths_1 = require("../engine/paths");
40
+ const user_assets_1 = require("../engine/user-assets");
40
41
  const memory_1 = require("../utils/memory");
41
42
  const logger_1 = require("../utils/logger");
42
43
  async function runInit(cwd, opts) {
@@ -59,8 +60,10 @@ async function runInit(cwd, opts) {
59
60
  await fs.ensureDir(path.join(root, ".handoff"));
60
61
  await fs.ensureDir((0, paths_1.orqintSpecDir)(cwd));
61
62
  await fs.ensureDir((0, paths_1.orqintArchiveDir)(cwd));
62
- const userAssetsCopied = await copyUserAssets(cwd, opts);
63
- if (userAssetsCopied > 0) {
63
+ const { copiedPaths: userAssetsCopied } = await (0, user_assets_1.applyUserAssetsInit)(cwd, {
64
+ forceUserAssets: !!opts.forceUserAssets,
65
+ });
66
+ if (userAssetsCopied.length > 0) {
64
67
  (0, logger_1.log)("info", "init_next_step", {
65
68
  hint: "Open ORQINT.md in your project root for the workflow overview.",
66
69
  });
@@ -76,43 +79,6 @@ async function runInit(cwd, opts) {
76
79
  await (0, memory_1.writeMemory)(cwd, memory);
77
80
  (0, logger_1.log)("info", "init_complete", { cwd, brownfield: !!opts.brownfield });
78
81
  }
79
- /** Installed package root (contains `user-assets/` next to `dist/`). */
80
- function packageRootDir() {
81
- return path.join(__dirname, "..", "..", "..");
82
- }
83
- /** Paths relative to package root → relative to consumer cwd. */
84
- const USER_ASSET_MANIFEST = [
85
- { from: path.join("user-assets", "AGENTS.md"), to: "AGENTS.md" },
86
- { from: path.join("user-assets", "ORQINT.md"), to: "ORQINT.md" },
87
- {
88
- from: path.join("user-assets", "cursor-rules", "orqint-workflow.mdc"),
89
- to: path.join(".cursor", "rules", "orqint-workflow.mdc"),
90
- },
91
- ];
92
- /** Copy shipped user assets; skip existing destinations unless `forceUserAssets`. Returns count copied. */
93
- async function copyUserAssets(cwd, opts) {
94
- const pkgRoot = packageRootDir();
95
- let copied = 0;
96
- for (const { from: relFrom, to: relTo } of USER_ASSET_MANIFEST) {
97
- const from = path.join(pkgRoot, relFrom);
98
- const to = path.join(cwd, relTo);
99
- if (!(await fs.pathExists(from))) {
100
- (0, logger_1.log)("warn", "init_user_asset_missing", { from });
101
- continue;
102
- }
103
- const destExists = await fs.pathExists(to);
104
- const shouldCopy = opts.forceUserAssets || !destExists;
105
- if (!shouldCopy) {
106
- (0, logger_1.log)("info", "init_user_asset_skipped_exists", { file: relTo });
107
- continue;
108
- }
109
- await fs.ensureDir(path.dirname(to));
110
- await fs.copy(from, to, { overwrite: opts.forceUserAssets });
111
- (0, logger_1.log)("info", "init_user_asset_copied", { file: relTo });
112
- copied += 1;
113
- }
114
- return copied;
115
- }
116
82
  async function seedFromRepo(cwd, memory) {
117
83
  const pkgPath = path.join(cwd, "package.json");
118
84
  if (await fs.pathExists(pkgPath)) {
@@ -0,0 +1,6 @@
1
+ export type SyncAssetsCliOptions = {
2
+ force: boolean;
3
+ migrate: boolean;
4
+ dryRun: boolean;
5
+ };
6
+ export declare function runSyncAssets(cwd: string, opts: SyncAssetsCliOptions): Promise<void>;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runSyncAssets = runSyncAssets;
37
+ const fs = __importStar(require("fs-extra"));
38
+ const paths_1 = require("../engine/paths");
39
+ const user_assets_1 = require("../engine/user-assets");
40
+ async function runSyncAssets(cwd, opts) {
41
+ const memPath = (0, paths_1.memoryJsonPath)(cwd);
42
+ if (!(await fs.pathExists(memPath))) {
43
+ throw new Error(`No Orqint project here (missing ${memPath}). Run: orqint init`);
44
+ }
45
+ const result = await (0, user_assets_1.applyUserAssetsSync)(cwd, {
46
+ force: opts.force,
47
+ migrate: opts.migrate,
48
+ dryRun: opts.dryRun,
49
+ });
50
+ const byPathWould = new Map(result.wouldDo.map((w) => [w.path, w.action]));
51
+ const skipReason = new Map(result.skippedPaths.map((s) => [s.path, s.reason]));
52
+ const copiedSet = new Set(result.copiedPaths);
53
+ for (const { to: relTo } of user_assets_1.USER_ASSET_MANIFEST) {
54
+ if (opts.dryRun) {
55
+ const action = byPathWould.get(relTo);
56
+ if (action === "copy") {
57
+ console.error(`[dry-run] Would update ${relTo}`);
58
+ }
59
+ else {
60
+ const r = skipReason.get(relTo) ?? "no change";
61
+ console.error(`[dry-run] Would leave ${relTo} unchanged (${r})`);
62
+ }
63
+ continue;
64
+ }
65
+ if (copiedSet.has(relTo)) {
66
+ console.error(`Updated ${relTo}`);
67
+ }
68
+ else {
69
+ const r = skipReason.get(relTo);
70
+ if (r) {
71
+ console.error(`Left ${relTo} unchanged (${r})`);
72
+ }
73
+ }
74
+ }
75
+ }
@@ -35,6 +35,11 @@ function fetchLatestFromNpm(name) {
35
35
  * Check npm registry for a newer version; optionally re-invoke via npx @latest.
36
36
  */
37
37
  function runUpdate(meta, install, forwardArgs) {
38
+ /** Subcommand after `--install` (e.g. `sync-assets`): always run via npx @latest so templates track the published CLI even when this binary already matches npm. */
39
+ if (install && forwardArgs.length > 0) {
40
+ const r = (0, child_process_1.spawnSync)("npx", ["--yes", `${meta.name}@latest`, ...forwardArgs], { stdio: "inherit", shell: process.platform === "win32" });
41
+ process.exit(r.status === null ? 1 : r.status);
42
+ }
38
43
  const latest = fetchLatestFromNpm(meta.name);
39
44
  if (!latest) {
40
45
  console.error(`Could not read latest version from npm for "${meta.name}".`);
@@ -1,5 +1,6 @@
1
1
  export declare function orqintRoot(cwd: string): string;
2
2
  export declare function memoryJsonPath(cwd: string): string;
3
+ export declare function userAssetsLockPath(cwd: string): string;
3
4
  export declare function handoffDir(cwd: string): string;
4
5
  export declare function promptsDir(cwd: string): string;
5
6
  export declare function orqintSpecDir(cwd: string): string;
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.orqintRoot = orqintRoot;
37
37
  exports.memoryJsonPath = memoryJsonPath;
38
+ exports.userAssetsLockPath = userAssetsLockPath;
38
39
  exports.handoffDir = handoffDir;
39
40
  exports.promptsDir = promptsDir;
40
41
  exports.orqintSpecDir = orqintSpecDir;
@@ -46,6 +47,9 @@ function orqintRoot(cwd) {
46
47
  function memoryJsonPath(cwd) {
47
48
  return path.join(orqintRoot(cwd), "core", "memory.json");
48
49
  }
50
+ function userAssetsLockPath(cwd) {
51
+ return path.join(orqintRoot(cwd), "core", "user-assets.lock.json");
52
+ }
49
53
  function handoffDir(cwd) {
50
54
  return path.join(orqintRoot(cwd), ".handoff");
51
55
  }
@@ -0,0 +1,46 @@
1
+ /** Installed package root (contains `user-assets/` next to `dist/`). */
2
+ export declare function packageRootDir(): string;
3
+ /** Paths relative to package root → relative to consumer cwd. */
4
+ export declare const USER_ASSET_MANIFEST: {
5
+ from: string;
6
+ to: string;
7
+ }[];
8
+ export type UserAssetsLockFile = {
9
+ version: number;
10
+ cliVersionWhenWritten: string;
11
+ assets: Record<string, {
12
+ sha256: string;
13
+ }>;
14
+ };
15
+ export declare function lockKeyForDest(relTo: string): string;
16
+ export declare function sha256OfFile(absPath: string): Promise<string>;
17
+ export declare function sha256OfBuffer(buf: Buffer): string;
18
+ export declare function readUserAssetsLock(cwd: string): Promise<UserAssetsLockFile | null>;
19
+ export declare function writeUserAssetsLock(cwd: string, lock: UserAssetsLockFile): Promise<void>;
20
+ export type UserAssetApplyResult = {
21
+ copiedPaths: string[];
22
+ skippedPaths: {
23
+ path: string;
24
+ reason: string;
25
+ }[];
26
+ wouldDo: {
27
+ path: string;
28
+ action: "copy" | "skip";
29
+ }[];
30
+ };
31
+ export type InitAssetOptions = {
32
+ forceUserAssets: boolean;
33
+ };
34
+ /**
35
+ * Init: copy missing assets (or all if forceUserAssets); update lock only for paths written.
36
+ */
37
+ export declare function applyUserAssetsInit(cwd: string, opts: InitAssetOptions): Promise<UserAssetApplyResult>;
38
+ export type SyncAssetOptions = {
39
+ force: boolean;
40
+ migrate: boolean;
41
+ dryRun: boolean;
42
+ };
43
+ /**
44
+ * Sync: safe update from package when on-disk file still matches lock; migrate/force overwrite all.
45
+ */
46
+ export declare function applyUserAssetsSync(cwd: string, opts: SyncAssetOptions): Promise<UserAssetApplyResult>;
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.USER_ASSET_MANIFEST = void 0;
37
+ exports.packageRootDir = packageRootDir;
38
+ exports.lockKeyForDest = lockKeyForDest;
39
+ exports.sha256OfFile = sha256OfFile;
40
+ exports.sha256OfBuffer = sha256OfBuffer;
41
+ exports.readUserAssetsLock = readUserAssetsLock;
42
+ exports.writeUserAssetsLock = writeUserAssetsLock;
43
+ exports.applyUserAssetsInit = applyUserAssetsInit;
44
+ exports.applyUserAssetsSync = applyUserAssetsSync;
45
+ const path = __importStar(require("path"));
46
+ const fs = __importStar(require("fs-extra"));
47
+ const crypto_1 = require("crypto");
48
+ const paths_1 = require("./paths");
49
+ const package_meta_1 = require("../package-meta");
50
+ const logger_1 = require("../utils/logger");
51
+ /** Installed package root (contains `user-assets/` next to `dist/`). */
52
+ function packageRootDir() {
53
+ return path.join(__dirname, "..", "..", "..");
54
+ }
55
+ /** Paths relative to package root → relative to consumer cwd. */
56
+ exports.USER_ASSET_MANIFEST = [
57
+ { from: path.join("user-assets", "AGENTS.md"), to: "AGENTS.md" },
58
+ { from: path.join("user-assets", "ORQINT.md"), to: "ORQINT.md" },
59
+ {
60
+ from: path.join("user-assets", "cursor-rules", "orqint-workflow.mdc"),
61
+ to: path.join(".cursor", "rules", "orqint-workflow.mdc"),
62
+ },
63
+ ];
64
+ function lockKeyForDest(relTo) {
65
+ return relTo.split(path.sep).join("/");
66
+ }
67
+ function sha256OfFile(absPath) {
68
+ return fs.readFile(absPath).then((buf) => (0, crypto_1.createHash)("sha256").update(buf).digest("hex"));
69
+ }
70
+ function sha256OfBuffer(buf) {
71
+ return (0, crypto_1.createHash)("sha256").update(buf).digest("hex");
72
+ }
73
+ async function readUserAssetsLock(cwd) {
74
+ const p = (0, paths_1.userAssetsLockPath)(cwd);
75
+ if (!(await fs.pathExists(p))) {
76
+ return null;
77
+ }
78
+ try {
79
+ const raw = await fs.readJson(p);
80
+ if (raw &&
81
+ typeof raw === "object" &&
82
+ typeof raw.version === "number" &&
83
+ raw.assets &&
84
+ typeof raw.assets === "object") {
85
+ return raw;
86
+ }
87
+ }
88
+ catch {
89
+ (0, logger_1.log)("warn", "user_assets_lock_read_failed", { path: p });
90
+ }
91
+ return null;
92
+ }
93
+ async function writeUserAssetsLock(cwd, lock) {
94
+ const p = (0, paths_1.userAssetsLockPath)(cwd);
95
+ await fs.ensureDir(path.dirname(p));
96
+ await fs.writeJson(p, lock, { spaces: 2 });
97
+ }
98
+ /**
99
+ * Init: copy missing assets (or all if forceUserAssets); update lock only for paths written.
100
+ */
101
+ async function applyUserAssetsInit(cwd, opts) {
102
+ const pkgRoot = packageRootDir();
103
+ const result = {
104
+ copiedPaths: [],
105
+ skippedPaths: [],
106
+ wouldDo: [],
107
+ };
108
+ const lock = (await readUserAssetsLock(cwd)) ?? {
109
+ version: 1,
110
+ cliVersionWhenWritten: "",
111
+ assets: {},
112
+ };
113
+ const meta = (0, package_meta_1.readPackageMeta)();
114
+ for (const { from: relFrom, to: relTo } of exports.USER_ASSET_MANIFEST) {
115
+ const from = path.join(pkgRoot, relFrom);
116
+ const to = path.join(cwd, relTo);
117
+ const key = lockKeyForDest(relTo);
118
+ if (!(await fs.pathExists(from))) {
119
+ (0, logger_1.log)("warn", "user_asset_bundled_missing", { from });
120
+ result.skippedPaths.push({ path: relTo, reason: "missing in package" });
121
+ continue;
122
+ }
123
+ const destExists = await fs.pathExists(to);
124
+ const shouldCopy = opts.forceUserAssets || !destExists;
125
+ if (!shouldCopy) {
126
+ (0, logger_1.log)("info", "init_user_asset_skipped_exists", { file: relTo });
127
+ result.skippedPaths.push({ path: relTo, reason: "already exists" });
128
+ continue;
129
+ }
130
+ await fs.ensureDir(path.dirname(to));
131
+ await fs.copy(from, to, { overwrite: opts.forceUserAssets });
132
+ const hash = await sha256OfFile(to);
133
+ lock.assets[key] = { sha256: hash };
134
+ lock.cliVersionWhenWritten = meta.version;
135
+ lock.version = 1;
136
+ (0, logger_1.log)("info", "init_user_asset_copied", { file: relTo });
137
+ result.copiedPaths.push(relTo);
138
+ }
139
+ if (result.copiedPaths.length > 0) {
140
+ await writeUserAssetsLock(cwd, lock);
141
+ }
142
+ return result;
143
+ }
144
+ /**
145
+ * Sync: safe update from package when on-disk file still matches lock; migrate/force overwrite all.
146
+ */
147
+ async function applyUserAssetsSync(cwd, opts) {
148
+ const pkgRoot = packageRootDir();
149
+ const result = {
150
+ copiedPaths: [],
151
+ skippedPaths: [],
152
+ wouldDo: [],
153
+ };
154
+ let lock = await readUserAssetsLock(cwd);
155
+ const meta = (0, package_meta_1.readPackageMeta)();
156
+ for (const { from: relFrom, to: relTo } of exports.USER_ASSET_MANIFEST) {
157
+ const from = path.join(pkgRoot, relFrom);
158
+ const to = path.join(cwd, relTo);
159
+ const key = lockKeyForDest(relTo);
160
+ if (!(await fs.pathExists(from))) {
161
+ result.skippedPaths.push({ path: relTo, reason: "missing in package" });
162
+ continue;
163
+ }
164
+ const bundledBuf = await fs.readFile(from);
165
+ const bundledHash = sha256OfBuffer(bundledBuf);
166
+ const destExists = await fs.pathExists(to);
167
+ let shouldCopy = false;
168
+ let skipReason = "";
169
+ if (opts.force || opts.migrate) {
170
+ shouldCopy = true;
171
+ }
172
+ else if (!destExists) {
173
+ shouldCopy = true;
174
+ }
175
+ else if (!lock || !lock.assets[key]) {
176
+ shouldCopy = false;
177
+ skipReason =
178
+ "file exists but not in lock (run: orqint sync-assets --migrate once, or --force)";
179
+ }
180
+ else {
181
+ const diskHash = await sha256OfFile(to);
182
+ if (diskHash !== lock.assets[key].sha256) {
183
+ shouldCopy = false;
184
+ skipReason = "you edited this file; left unchanged";
185
+ }
186
+ else if (diskHash === bundledHash) {
187
+ shouldCopy = false;
188
+ skipReason = "already up to date";
189
+ }
190
+ else {
191
+ shouldCopy = true;
192
+ }
193
+ }
194
+ if (opts.dryRun) {
195
+ result.wouldDo.push({
196
+ path: relTo,
197
+ action: shouldCopy ? "copy" : "skip",
198
+ });
199
+ if (!shouldCopy) {
200
+ result.skippedPaths.push({
201
+ path: relTo,
202
+ reason: skipReason || "no change",
203
+ });
204
+ }
205
+ continue;
206
+ }
207
+ if (!shouldCopy) {
208
+ result.skippedPaths.push({
209
+ path: relTo,
210
+ reason: skipReason || "no change",
211
+ });
212
+ continue;
213
+ }
214
+ await fs.ensureDir(path.dirname(to));
215
+ await fs.writeFile(to, bundledBuf);
216
+ const newHash = sha256OfBuffer(bundledBuf);
217
+ if (!lock) {
218
+ lock = {
219
+ version: 1,
220
+ cliVersionWhenWritten: meta.version,
221
+ assets: {},
222
+ };
223
+ }
224
+ lock.assets[key] = { sha256: newHash };
225
+ lock.cliVersionWhenWritten = meta.version;
226
+ lock.version = 1;
227
+ result.copiedPaths.push(relTo);
228
+ (0, logger_1.log)("info", "user_asset_sync_copied", { file: relTo });
229
+ }
230
+ if (!opts.dryRun && lock && result.copiedPaths.length > 0) {
231
+ await writeUserAssetsLock(cwd, lock);
232
+ }
233
+ return result;
234
+ }
@@ -40,6 +40,7 @@ const think_1 = require("./commands/think");
40
40
  const plan_1 = require("./commands/plan");
41
41
  const package_meta_1 = require("./package-meta");
42
42
  const update_1 = require("./commands/update");
43
+ const sync_assets_1 = require("./commands/sync-assets");
43
44
  function printHelp() {
44
45
  const { name, version } = (0, package_meta_1.readPackageMeta)();
45
46
  console.log(`${name} ${version} — Phase 1 (Cursor-native)
@@ -49,6 +50,7 @@ No model API keys in this tool: use Cursor on the generated handoff files, then
49
50
  Commands:
50
51
  orqint version | --version | -V
51
52
  orqint update [--install] [<args>...] check npm; --install runs npx ${name}@latest <args>
53
+ orqint sync-assets [--force] [--dry-run] [--migrate] refresh AGENTS/ORQINT/rules from package when safe
52
54
  orqint init [--brownfield] [--migrate-legacy] [--force] [--force-user-assets]
53
55
  orqint think prepare "<intent>" [--override key=value ...]
54
56
  orqint think apply --file <path>
@@ -56,6 +58,7 @@ Commands:
56
58
  orqint plan apply --file <path> [--force]
57
59
 
58
60
  Examples:
61
+ orqint update --install sync-assets run latest CLI then sync templates (good after npm update)
59
62
  orqint init --brownfield
60
63
  orqint think prepare "Build a task dashboard" --override mobile_first=true
61
64
  orqint think apply --file orqint/.handoff/think.response.json
@@ -124,6 +127,17 @@ async function main() {
124
127
  (0, update_1.runUpdate)((0, package_meta_1.readPackageMeta)(), install, rest);
125
128
  return;
126
129
  }
130
+ if (cmd === "sync-assets") {
131
+ const args = argv.slice(1);
132
+ const force = takeFlag(args, "--force");
133
+ const migrate = takeFlag(args, "--migrate");
134
+ const dryRun = takeFlag(args, "--dry-run");
135
+ if (args.length > 0) {
136
+ throw new Error(`Unknown arguments: ${args.join(" ")}`);
137
+ }
138
+ await (0, sync_assets_1.runSyncAssets)(cwd, { force, migrate, dryRun });
139
+ process.exit(0);
140
+ }
127
141
  if (cmd === "init") {
128
142
  const args = argv.slice(1);
129
143
  const forceUserAssets = takeFlag(args, "--force-user-assets");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orqint/cli",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "CLI orchestration for Cursor-native think/plan workflows (Orqint)",
5
5
  "keywords": [
6
6
  "orqint",
@@ -22,14 +22,31 @@ Use `npx @orqint/cli` or a local/global `orqint` binary the same way.
22
22
  | Plan handoff | `orqint plan prepare` |
23
23
  | In Cursor | Same pattern → `orqint/.handoff/plan.response.json` |
24
24
  | Merge plan | `orqint plan apply --file orqint/.handoff/plan.response.json` |
25
+ | Refresh templates after package update | `orqint sync-assets` (see below) |
25
26
 
26
27
  Follow the JSON schema embedded in each handoff file. Honor `memory.override` when the sliced context includes it.
27
28
 
29
+ ## Keeping `AGENTS.md`, `ORQINT.md`, and Cursor rules up to date
30
+
31
+ After **`npm update @orqint/cli`**, run **`orqint sync-assets`**. Orqint updates each managed file **only if** it still matches what Orqint last wrote (tracked in **`orqint/core/user-assets.lock.json`**). If you edited a file, Orqint **leaves it alone** and tells you. **`memory.json`** is not changed.
32
+
33
+ One-liner with the **latest** published CLI (useful if your global `orqint` is old):
34
+
35
+ ```bash
36
+ orqint update --install sync-assets
37
+ ```
38
+
39
+ - **`--dry-run`** — show actions only.
40
+ - **`--migrate`** — once per old project: replace all managed templates from the package and create/update the full lock.
41
+ - **`--force`** — overwrite every managed file and refresh the lock (power users).
42
+
43
+ Commit **`orqint/core/user-assets.lock.json`** with the repo so teammates share the same baseline.
44
+
28
45
  ## Optional flags (`init`)
29
46
 
30
47
  - `orqint init --brownfield` — seed `memory.json` hints from `package.json` / `README.md` when present.
31
48
  - `orqint init --force` — re-initialize **Orqint runtime** (e.g. `memory.json`, layout). Does **not** overwrite existing **`AGENTS.md`**, **`ORQINT.md`**, or **`.cursor/rules/orqint-workflow.mdc`** in your project.
32
- - `orqint init --force-user-assets` — refresh those Orqint-shipped files from the package (overwrites the paths above). Use when you want the latest templates from your installed CLI version.
49
+ - `orqint init --force-user-assets` — refresh those Orqint-shipped files from the package (overwrites the paths above). Use when you want the latest templates from your installed CLI version. Prefer **`sync-assets`** for routine updates after `npm update` (does not reset memory).
33
50
 
34
51
  ## Package reference
35
52