driftseal 1.1.6 → 1.1.7

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
@@ -73,8 +73,9 @@ driftseal skill install --target kimi-code --scope global
73
73
  | `cursor` | `.cursor/skills/use-driftseal` | `~/.cursor/skills/use-driftseal` |
74
74
 
75
75
  Use `--root <repository>` to select a project when running the installer
76
- elsewhere. Repeated installs of identical content are no-ops; a different
77
- existing skill requires `--force`. MCP and lifecycle hooks are optional
76
+ elsewhere. Repeated installs of identical content are no-ops, and a skill left
77
+ by an earlier DriftSeal release is upgraded in place; only a skill this
78
+ installer never wrote requires `--force`. MCP and lifecycle hooks are optional
78
79
  adapters; enable them only for a concrete host constraint or reminder need, not
79
80
  as additional policy layers.
80
81
 
package/README.zh-CN.md CHANGED
@@ -71,7 +71,8 @@ driftseal skill install --target kimi-code --scope global
71
71
  | `cursor` | `.cursor/skills/use-driftseal` | `~/.cursor/skills/use-driftseal` |
72
72
 
73
73
  如果不在目标 repository 中执行,用 `--root <repository>` 明确指定项目。
74
- 重复安装相同内容不会产生改动;目标位置已有不同版本时必须显式传入 `--force`。
74
+ 重复安装相同内容不会产生改动;旧版本 DriftSeal 装下的 skill 会被直接原地升级,
75
+ 只有安装器从未写过的 skill 才需要显式传入 `--force`。
75
76
  MCP 与 lifecycle hook 都是可选适配层;只有确实存在 host 限制或提醒需求时
76
77
  才启用,不要把它们叠成额外的 policy 层。
77
78
 
package/bin/driftseal.js CHANGED
@@ -1984,6 +1984,27 @@ function installMcp(request) {
1984
1984
 
1985
1985
  const SKILL_NAME = 'use-driftseal';
1986
1986
 
1987
+ /*
1988
+ * Every skill tree DriftSeal has ever bundled, oldest first, as skillTreeDigest
1989
+ * hashes. `skill install` treats a directory matching one of these as its own
1990
+ * earlier output and upgrades it in place; anything else is someone's local
1991
+ * skill that only --force may replace. Append the new digest whenever the
1992
+ * bundled skill changes, otherwise the next release cannot upgrade this one;
1993
+ * the "bundled use-driftseal skill is a known release" test fails until you do.
1994
+ */
1995
+ const SKILL_RELEASE_DIGESTS = new Set([
1996
+ 'e996627c96edc7c09599bc454c4225a416aa89e50aef14d4dd569dd21454e882', // 1b76215 initial commit
1997
+ '5f702545b18e117bafcea669b48cdb3b31c98079f97eaa084fd3b4ceff9500e8', // 5c2ec74 intents only for rollback-worthy changes
1998
+ '77f6365590ff181ee6be003930dd1696c363e213368de0f4e89962555af3fabe', // 56380a8 local MCP server
1999
+ 'b7e2310eaf20b50b5ec28531471965607e7e070a4f58263e29408b2cd5cdfb11', // 1cc77f6 reclaim markers
2000
+ '08bc63b8dcf0f4f078252179a3fc2ca4ef2632ecb5b9dfe3782a452c3202c2c4', // de4a8a1 skill slimmed into a usage guide
2001
+ '8523459ff81cf0b97a36b30d216956a58d8a3b3b9760f455d7ea334604da335a', // 3a1d6e0 protocol v7 resume semantics
2002
+ 'cc98b9348ec222320bfcd285ba3f1f499a42d15b31e9b1d83c35f0206b2d5ba9', // ca16785 CLI-first skill integration
2003
+ '0fd870f8c1b81f8386d986d64742679d56cd1d317c02890830c81876eb9227d6', // da8afd2 1.1.0 absorb
2004
+ '72ddea79940bdf2bce66d491888f11423ae1bd383e1b511028fda617e6f6fb27', // f395778 1.1.6 parked intents
2005
+ 'df8bc7035de1a19faf307c92f9bb0f4052e683d1a94881c2c5d5cbef48b67568', // dc9899d 1.1.7 parked intents in absorb (current)
2006
+ ]);
2007
+
1987
2008
  function skillInstallUsage() {
1988
2009
  return 'usage: driftseal skill install --target <codex|kimi-code|opencode|claude-code|cursor> [--scope project|global] [--root <repository>] [--force]';
1989
2010
  }
@@ -2046,7 +2067,13 @@ function parseSkillInstallRequest(argv) {
2046
2067
  };
2047
2068
  }
2048
2069
 
2049
- function directoryDigest(directory) {
2070
+ /*
2071
+ * Identifies a skill tree by its contents alone: relative paths joined with "/"
2072
+ * and file bytes, never file modes or timestamps. The digest therefore stays
2073
+ * stable across platforms, checkouts, and npm tarballs, which is what lets
2074
+ * SKILL_RELEASE_DIGESTS recognize a skill DriftSeal installed earlier.
2075
+ */
2076
+ function skillTreeDigest(directory) {
2050
2077
  if (!fs.existsSync(directory)) return null;
2051
2078
  const digest = crypto.createHash('sha256');
2052
2079
 
@@ -2055,12 +2082,12 @@ function directoryDigest(directory) {
2055
2082
  if (stat.isDirectory()) {
2056
2083
  digest.update(`directory\0${relative}\0`);
2057
2084
  for (const name of fs.readdirSync(current).sort()) {
2058
- visit(path.join(current, name), relative ? path.join(relative, name) : name);
2085
+ visit(path.join(current, name), relative ? `${relative}/${name}` : name);
2059
2086
  }
2060
2087
  return;
2061
2088
  }
2062
2089
  if (stat.isFile()) {
2063
- digest.update(`file\0${relative}\0${stat.mode & 0o777}\0`);
2090
+ digest.update(`file\0${relative}\0`);
2064
2091
  digest.update(fs.readFileSync(current));
2065
2092
  digest.update('\0');
2066
2093
  return;
@@ -2069,7 +2096,7 @@ function directoryDigest(directory) {
2069
2096
  digest.update(`symlink\0${relative}\0${fs.readlinkSync(current)}\0`);
2070
2097
  return;
2071
2098
  }
2072
- digest.update(`other\0${relative}\0${stat.mode}\0`);
2099
+ digest.update(`other\0${relative}\0`);
2073
2100
  }
2074
2101
 
2075
2102
  visit(directory, '');
@@ -2096,15 +2123,18 @@ function installSkill(request) {
2096
2123
  fail(`bundled ${SKILL_NAME} skill is missing from this DriftSeal installation: ${sourceDir}`);
2097
2124
  }
2098
2125
 
2099
- const sourceDigest = directoryDigest(sourceDir);
2100
- const existingDigest = directoryDigest(skillDir);
2126
+ const sourceDigest = skillTreeDigest(sourceDir);
2127
+ const existingDigest = skillTreeDigest(skillDir);
2101
2128
  if (existingDigest === sourceDigest) {
2102
2129
  printLine(`${SKILL_NAME} skill is already installed for ${targetLabel} (${scope}): ${skillDir}`);
2103
2130
  return { changed: false, target, scope, root, skillDir };
2104
2131
  }
2105
- if (existingDigest !== null && !force) {
2132
+ // An untouched skill from an earlier DriftSeal upgrades on its own; only a
2133
+ // skill this installer never wrote needs the operator to confirm with --force.
2134
+ const upgraded = existingDigest !== null && SKILL_RELEASE_DIGESTS.has(existingDigest);
2135
+ if (existingDigest !== null && !upgraded && !force) {
2106
2136
  fail(
2107
- `${targetLabel} already has a different ${SKILL_NAME} skill at ${skillDir}; ` +
2137
+ `${targetLabel} already has a ${SKILL_NAME} skill DriftSeal did not install at ${skillDir}; ` +
2108
2138
  're-run with --force to replace it'
2109
2139
  );
2110
2140
  }
@@ -2134,9 +2164,12 @@ function installSkill(request) {
2134
2164
  throw err;
2135
2165
  }
2136
2166
 
2137
- printLine(`Installed ${SKILL_NAME} skill for ${targetLabel} (${scope}): ${skillDir}`);
2167
+ printLine(
2168
+ `${upgraded ? 'Upgraded' : 'Installed'} ${SKILL_NAME} skill for ` +
2169
+ `${targetLabel} (${scope}): ${skillDir}`
2170
+ );
2138
2171
  if (scope === 'project') printLine(`Repository root: ${root}`);
2139
- return { changed: true, target, scope, root, skillDir };
2172
+ return { changed: true, upgraded, target, scope, root, skillDir };
2140
2173
  }
2141
2174
 
2142
2175
  const HOOK_TARGETS = ['kimi-code', 'claude-code', 'codex'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "driftseal",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "description": "Seal intent, verification, and decisions into an auditable workflow for agentic coding",
5
5
  "keywords": [
6
6
  "driftseal",